Let's consider an example wherein I have to model the following:
- Class to schedule exams for a Student, lets call it
StudentExamScheduler
- Class to schedule exam for a Class. Let's call it
ClassExamScheduler
and currently it depends on the logic provided byStudentExamScheduler
.
So since both the classes schedule exams, I created an interface ExamScheduler
which both the classes would implement. Reason being:
- They are essentially scheduling exams
- would give me the flexibility to swap the schedulers in-and-out as functionality changes.
This is how the interface would look:
interface ExamScheduler {
scheduleExam(data);
}
Now each of my concrete class implements this interface and ClassExamScheduler
now uses the StudentExamScheduler
to delegate some responsibilities.
class ClassExamScheduler implements ExamScheduler {
private ExamScheduler studentExamScheduler;
public scheduleExam() {
studentExamScheduler.scheduleExam();
// do other stuff
}
}
Is this a valid use of Interface & Composition? Typically I have seen Interface being used for polymorphism. But in this use case I use it to provide me flexibility to inject new classes when the requirements change.
-
What you have written here is an example of the Decorator design pattern, one of the most common uses of composition and polymorphism. There's certainly nothing wrong with this approach.Jules– Jules2016年07月07日 08:25:56 +00:00Commented Jul 7, 2016 at 8:25
-
@Jules yeah it looks like a variant of decorator without any inheritance stuff.AgentX– AgentX2016年07月07日 12:09:10 +00:00Commented Jul 7, 2016 at 12:09
2 Answers 2
My two cents:
- There's no contradiction between the use of interfaces and composition.
- You are using polymorphism. Dependency injection and delegation are possible because of polymorphism.
A couple of observations:
- The
studentExamScheduler
should be namedexamScheduler
. Since you will be injecting it you should not name the variable after an specific implementos ofExamScheduler
. - Consider creating a more generic
Scheduler
interface, with a schedule(ScheduleData data); such an interface could be implemented by any type of scheduler likeLessonScheduler
,MeetingScheduler
etc.
-
My bad, I just tried to come up with a simple example of the problem I am trying to solve. Added the implements clause.AgentX– AgentX2016年07月06日 14:19:12 +00:00Commented Jul 6, 2016 at 14:19
-
@AgentX I deleted that observation and changed it for another one related to a more generic interface.Tulains Córdova– Tulains Córdova2016年07月06日 14:38:10 +00:00Commented Jul 6, 2016 at 14:38
-
Yes, it makes sense. I have tried to keep the interface generic in regards with the Business Process at hand.AgentX– AgentX2016年07月06日 15:42:49 +00:00Commented Jul 6, 2016 at 15:42
Using interfaces like this is usually an easy and clean way to provide flexibility. For example you could change the implementation of a ClassExamScheduler
instance at runtime without modifying source code. This is possible by assigning studentExamScheduler
another type of ExamScheduler
that provide some other method of scheduling. This kind of polymorphism is one of the main benefits of using interfaces.
However
I don't see any real benefits of having ClassExamScheduler
implement ExamScheduler
. I see where you are going with the aspect of composition; since both classes schedule exams, they could implement a common method.
But this means that a ClassExamScheduler
can implement it's .scheduleExam(..)
-method by using another ClassExamScheduler
instance as a middleman, which to me seems kind of odd and unnecessarily complicated.
Second, this structure could cause program failure if used without caution. If a ClassExamScheduler
would implement a closed chain of ClassExamSchedulers
like:
ClassExamScheduler A, B, C;
A.studentExamScheduler = B;
B.studentExamScheduler = C;
C.studentExamScheduler = A;
Or even itself:
ClassExamScheduler A;
A.studentExamScheduler = A;
You would end up with a recursive loop that would end in a stack overflow.
Regarding composition
I think that the subtle distinction here is that StudentExamScheduler
schedules an exam and ClassExamScheduler
uses an ExamScheduler
(i.e. an StudentExamScheduler
) to schedule an exam, indirectly.
-
I don't think that I understand your answer completely. In the Business Problem I am trying to solve, these
StudentExamScheduler
andClassExamScheduler
are a bit different.ClassExamScheduler
currently needs a specific way of scheduling student's exams, the functionality is provided byStudentExamScheduler
. NowStudentExamScheduler
currently depends on specific framework which might change tomorrow. So using this approach I would get the flexibility that you mentioned.AgentX– AgentX2016年07月06日 15:48:04 +00:00Commented Jul 6, 2016 at 15:48 -
Is there something specific in my answer that you want me to make clear? My main point was that
ClassExamScheduler
won't have to implementExamScheduler
to achieve your desired functionality. Rather, makingClassExamScheduler
implementExamScheduler
introduces risks of misuse since aClassExamScheduler
could provide this functionality, i.e. an instance ofClassExamScheduler
could be yourprivate ExamScheduler studentExamScheduler;
. To utilize the interface you'd instead want to have different classes for different frameworks instead of changingStudentExamScheduler
every time.Supreme Barbarian– Supreme Barbarian2016年07月06日 18:24:15 +00:00Commented Jul 6, 2016 at 18:24