Doesn't dependency inversion lead to code duplication?

185 views
Skip to first unread message

Srikanth Venkatesh

unread,
Jan 24, 2016, 5:38:56 AM1/24/16
to Clean Code Discussion
Module "domain" (a higher level module) uses "schedules" (a low level module). For dependency inversion, I provided an interface definition in "domain" module which "schedules" module plugs into.

package domain.plugins;

public class SchedulePlugin {

   
private static Schedule schedulePlugin;

   
public static void plugIn(Schedule schedulePlugin) {
        schedulePlugin
= schedulePlugin;
   
}

   
public interface Schedule {
       
enum ScheduleType { TYPE1, TYPE2, TYPE3 }
       
List<Integer> getSchedulesOfType(ScheduleType type);
   
}
}

The "schedules" module implements the above "SchedulePlugin.Schedule" interface. Notice the enum ScheduleType. This is a copy of the ScheduleType in schedules module. Isn't that code duplication... or have I done dependency inversion incorrectly?

For simplicity, I have used Integer (as return of getSchedules). However, the return type is again something that's defined in schedules domain. That is, getSchedules gets List<ScheduleEntry>, ScheduleEntry gets TimeBasedEntry, TimeBasedEntry gets TextValue... all these are defined in schedules module... do I have to provide interfaces (which are replicas of ones defined in schedules module) for all of them - thus duplicating code?

Antônio Carvalho Jr.

unread,
Jan 24, 2016, 5:26:31 PM1/24/16
to clean-code...@googlegroups.com
Hey, there.

Why do you have a "ScheduleType in schedules module"? Why not use that domain.plugins.SchedulePlugin.Schedule.ScheduleType enum directly?

I may be misunderstanding what you are saying. Are you trying to not use domain classes/interfaces inside the schedules module?
When you provide interfaces at the domain module and other modules just implement that, this is not code duplication.

Through Dependency Inversion, the domain says "what" it needs by creating interfaces, leaving the "how" to the schedules module.

If the domain needs domain.plugins.SchedulePlugin.Schedule#getSchedulesOfType(...) to return List<ScheduleEntry>, then it absolute should. If ScheduleEntry is a domain class, then the schedules module just uses it (no need to duplicate it). If ScheduleEntry is just an interface, then schedules module just implements it through other classes, no duplication here either.

I must say I didn't fully understand what the problem was exactly. Just answered to get the ball rolling.

-- acdc


--
The only way to go fast is to go well.
---
You received this message because you are subscribed to the Google Groups "Clean Code Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clean-code-discu...@googlegroups.com.
To post to this group, send email to clean-code...@googlegroups.com.
Visit this group at https://groups.google.com/group/clean-code-discussion.

Norbert Nemes

unread,
Jan 24, 2016, 5:39:37 PM1/24/16
to Clean Code Discussion
If the domain already defines a public enum in the interface it works with, why do you have to redefine it in the implementation? I have a feeling that your problem is trying to retrofit a bottom-up implementation to a top down design and you forgot to refactor :).

Norbert Nemes

unread,
Jan 24, 2016, 5:42:06 PM1/24/16
to Clean Code Discussion
Argh! You've beaten me to the reply! Lol!

Srikanth Venkatesh

unread,
Jan 25, 2016, 1:22:37 AM1/25/16
to Clean Code Discussion
Hi Antonio Carvalho Jr,

Sorry for being vague about the problem.


Why do you have a "ScheduleType in schedules module"? Why not use that domain.plugins.SchedulePlugin.Schedule.ScheduleType enum directly?

schedules is more like a low level reusable custom component / library that's used by a number of other modules. There will be new ScheduleTypes in the future that schedules module provides support for - which higher level modules may / may not need.

I may be misunderstanding what you are saying. Are you trying to not use domain classes/interfaces inside the schedules module?
No... you understood right - I am trying to not use schedules classes/interfaces in domain.


When you provide interfaces at the domain module and other modules just implement that, this is not code duplication.
Through Dependency Inversion, the domain says "what" it needs by creating interfaces, leaving the "how" to the schedules module.
Understood. Correct me if I am wrong - if another higher level module, say domain-2 comes along, it too says what it needs by creating interfaces, leaving the how to the schedules module.

If the domain needs domain.plugins.SchedulePlugin.Schedule#getSchedulesOfType(...) to return List<ScheduleEntry>, then it absolute should. If ScheduleEntry is a domain class, then the schedules module just uses it (no need to duplicate it). If ScheduleEntry is just an interface, then schedules module just implements it through other classes, no duplication here either.
Understood. ScheduleEntry is just an interface. So, schedules module just implements it. So, when another higher module, again say domain-2 comes along, domain-2's interfaces are implemented by schedules module.

I must say I didn't fully understand what the problem was exactly.
Well, 2 problems
1. Since the interfaces like Schedule, ScheduleEntry etc are defined by all higher level modules that use schedules module (refer diagram), the interfaces are duplicated across the users. I thought that was duplication. I guess that's a wrong assumption since the implementation is not duplicated.
2. Not clear on this part still - ScheduleType enum is a core part of the schedules module and it defines the types supported by schedules module. (Refer diagram) Every higher level user of schedules module needs services for a subset of types in ScheduleType. So, If ScheduleType is defined in domain instead of schedules module, another higher level module wanting to use the schedules module will have to depend on the domain module... That's bad, right? How do I solve this? Enum is a bad data structure choice here?


Thanks,
Srikanth
To unsubscribe from this group and stop receiving emails from it, send an email to clean-code-discussion+unsub...@googlegroups.com.

Srikanth Venkatesh

unread,
Jan 25, 2016, 1:38:18 AM1/25/16
to Clean Code Discussion
Hi Norbert,

I am pasting a few lines from my previous post,
ScheduleType enum is a core part of the schedules module and it defines the types supported by schedules module. (Refer diagram in previous post) Every higher level user of schedules module needs services for a subset of types in ScheduleType. So, If ScheduleType is defined in domain instead of schedules module, another higher level module wanting to use the schedules module will have to depend on the domain module... That's bad, right? How do I solve this? Enum is a bad data structure choice here?

Norbert Nemes

unread,
Jan 25, 2016, 1:53:47 AM1/25/16
to Clean Code Discussion
The other high level stuff should use the schedule interface (which defines the enum) not the concrete implementation. Depending on interfaces is not bad. For the methods, you can have a base class that implements the common ones and derivatives implementing the variance. Again, no duplication.

Jacek Bilski

unread,
Jan 26, 2016, 10:02:27 AM1/26/16
to clean-code...@googlegroups.com
Hi Srikanth Venkatesh,

On 25.01.2016 07:22, Srikanth Venkatesh wrote:
> 2. Not clear on this part still - ScheduleType enum is a core part of
> the schedules module and it defines the types supported by schedules
> module. (Refer diagram) Every higher level user of schedules module
> needs services for a subset of types in ScheduleType. So, If
> ScheduleType is defined in domain instead of schedules module, another
> higher level module wanting to use the schedules module will have to
> depend on the domain module... That's bad, right? How do I solve this?
> Enum is a bad data structure choice here?

I have a feeling, that the problem here is a different one. You
mentioned, that there are different modules that are different subsets
of those schedules. So maybe there shouldn't be a single interface, but
multiple ones, each one specific to each client module. Then the
schedules module could implement all of them, but each client would only
know the part it is interested in. I cannot tell though, if that's
possible or desirable, because I don't know how much overlaps there are.
If none or little, you could try to go the way I described. If not maybe
your design is OK, just your business requirements are forcing you to go
this way. As always, there are compromises to make, just try to select
the best/least awkward one.

--
Pozdrawiam / Best regards / Mit freundlichen Grüßen

Jacek Bilski

Srikanth Venkatesh

unread,
Jan 26, 2016, 11:10:51 AM1/26/16
to Clean Code Discussion
Hi Jacek Bilski,


You mentioned, that there are different modules that are different subsets
of those schedules. So maybe there shouldn't be a single interface, but
multiple ones

Yes... I was thinking about the same thing - different interfaces for different modules, implemented by one schedules module.

I cannot tell though, if that's
possible or desirable, because I don't know how much overlaps there are.
If none or little, you could try to go the way I described.
I think that's the only way I am supposed to go. Otherwise, it'll be the start of higher level modules depending on lower modules. There will be a few overlaps (which I termed as "duplication" (not entirely true as pointed out by Antonio & Norbert)). A few overlaps like the enum type I previously described. I guess it's okay to have a few overlaps with adapters for each, than to ditch dependency inversion, like you suggest.

Regards,
Srikanth

Reply all
Reply to author
Forward
0 new messages