0

Imagine I have an object that could be any of a number of different types that all derive from a single base type. I want to create a second object which is related to the first object in the sense that its type is dependant on the type of the first object. The type of the second object will however always derive from a second base class.

My go to solution would be to have the first object create the second object through a virtual method.

In this particular situation however, we do not want the two classes to be coupled at all. Neither object should have any existence of the other object.

This means there must be some kind of third-party mediator object creating the second object based on the first objects type.

My next chosen solution would therefore be to have this mediator be a factory which looked specifically at the type of the first object and instantiated the second object based upon it.

To me this works, but seems not an ideal solution. If a factory is creating the first object then whenever a new derived type is added two factories must be updated and a new connection made.

Are there any common design patterns that could help with this and make the code cleaner?

Edit: As mentioned by @MustehssunIqbal. This problem is a classic case of parallel hierarchies. The problem with solving this by creating a bridge or similar method is that the two classes need to remain decoupled.

A concrete example is provided:

Lets say I have a base processor class that is inherited by many processor sub-classes. This is fine for the current application. A new application however also wants to use these classes and this is a GUI application. This application wants to create a GUI element for each processor and each individual type of processor must also have a specific GUI type.

Now to stop the GUI's being required in the first application, the processor and GUI classes must be decoupled.

WalleyM
  • 172
  • 7
  • Is this mapping between the first and second objects dynamic or a static configuration is fine? – João Dias Aug 24 '21 at 23:49
  • Static. It is fine if the mapping is predefined. – WalleyM Aug 24 '21 at 23:53
  • How did the first object get created? – Adrian K Aug 25 '21 at 03:23
  • Potentially through a factory or even just through regular specified construction. The main point is that the first object is alive before the second object has been created. – WalleyM Aug 25 '21 at 07:38
  • 1
    Need more details to answer this question - because from what you told, it seems like you're encountering parallel hierarchies - so I would go with the Bridge pattern. But if there is an object structure and you want to do different things on that structure, and you have to create methods for each type of object in that structure - then I would go with the Visitor pattern. :) Kindly share more details so that we can know what is the exact type of problem you're facing. – Mustehssun Iqbal Aug 25 '21 at 14:49
  • Sorry but this is a bit confusing, you say 'I want to create a second object which is related to the first object in the sense that its type is dependant on the type of the first object', but then you say 'we do not want the two classes to be coupled at all'. What kind of 'dependence' is acceptable? – Adrian K Aug 25 '21 at 20:27
  • Furthermore, I'm confused by: 'different types that all derive from a single base type' vs 'The type of the second object will however always derive from a second base class'. – Adrian K Aug 25 '21 at 20:31
  • 1
    @MustehssunIqbal Thanks for that I will do some research and add more detail on the points you mentioned. – WalleyM Aug 25 '21 at 21:01
  • @AdrianK The two objects should have no references or even any idea that the other exists. On your second point, there are two base classes. Both have a set of derived classes. For each derived first class there is also a derived second class. – WalleyM Aug 25 '21 at 21:03
  • Glad you got an answer @WalleyM. Just out of interest, what technologies are in use? Looking at your edit, if the GUI and non-GUI applications are different, segregation at design-time by sharing code libraries, rather than segregation at runtime via OO concepts, might also be an option. – Adrian K Aug 27 '21 at 01:09

3 Answers3

2

Thanks for the details. It seems like the case of parallel hierarchies. You have many UI elements like button, list - but when it comes to draw these, you have three buttons for three processors, three TextAreas for three processors, and so on.

Parallel hierarchies in UI layer for each type of processor

As mentioned in the comment, I would suggest implementing the Bridge pattern, where the things specific to different processors can be abstracted. ProcessorRenderer (diagram below) is the abstracted thing that can be used by the UI layer.

Bridge between UI elements (Glyph) and ProcessorRenderer

Another similar approach would be to use an Application Facade layer. As you mentioned in your edit, you need to separate the Processor hierarchy from the UI layer. I also agree with this. I wouldn't want the UI layer to know anything about the Processors (or anything from the Domain layer). UI layer should be dumb and it should just draw things like buttons, lists, radio buttons, etc. So I would suggest to introduce an Application Facade layer, which would know about the Processor hierarchy from the Domain layer, and would know different types of UI elements from the UI layer, but should not know "how to draw the UI elements" -> that part should only be known by the dumb UI layer. UI layer would be testable irrespective of the Domain, and Domain doesn't know anything about the UI. What the Application Facade layer contains is the conversion of Domain layer objects into data structures that can be used by the UI layer. There's a lot more about Application Facades you can read about here: https://www.martinfowler.com/apsupp/appfacades.pdf

Bridge pattern: https://en.wikipedia.org/wiki/Bridge_pattern

I see Application Facade as a special case of Bridge pattern here, except that the Application Facade (ProcessorRenderer) has a dependency on the UI layer, while in the Bridge pattern the dependency is the other way around. But that's just my opinion.

I dismissed the idea of using the Visitor pattern here since you mentioned in the edit that you want to completely decouple the UI layer from the Processor hierarchy. Also because if all that is done in the hierarchy is rendering, then there is just one impl of Visitor, i.e. RenderingVisitor. That would be completely okay, but I think it would be an overkill for making it a visitor. However, if you have many such different operations on the individual types of processors, where each operation is different for each type of processor, then I would recommend using the Visitor pattern instead. :)

Visitor pattern:https://en.wikipedia.org/wiki/Visitor_pattern#:~:text=In%20object%2Doriented%20programming%20and,structures%20without%20modifying%20the%20structures.

Mustehssun Iqbal
  • 576
  • 3
  • 19
  • This is great, thank you so much. I am going to respond to each in separate comments. – WalleyM Aug 26 '21 at 22:23
  • Firstly, the bridge solution. This seems like the most logical solution. However, I am still concerned about getting into a situation where I would have to create two factories. One for the processor and then another that creates the right ProcessorRenderer for that specific processor type. – WalleyM Aug 26 '21 at 22:30
  • 1
    The facade pattern also seems logical. I agree with you that in this case it is a specialised bridge. This could be used to solve the multiple factory problem I mentioned above. I don't think it should know about the different UI elements. I think that is too specific. Just as it generalises instructions to the processor it could also generalise instructions to the GUI (e.g. represent that this has happened in the processor). – WalleyM Aug 26 '21 at 22:39
  • That is the correct way to use the Application Facade. You are right about factories - you still have to create factories for each ProcessorRenderer. But if you applied factory to the previous design, then that would be n factories per processor, where n is the number of UI elements! Factory per processer is supposed to happen anyway, as the outward structure of Application Facade is very similar to the Domain layer, since you're creating a converter for each domain object. :) – Mustehssun Iqbal Aug 28 '21 at 11:56
1

Given that a static mapping is ok, I would keep it as a configuration file with the pairs of the fully qualified name of the classes.

Then I would read this configuration into a Map structure at runtime with the key being the first or main object. For each object, I would search Map structure and find the corresponding associated second object given the first object's fully qualified name. After this I just need to instatiate the second object:

Class<?> clazz = Class.forName(className);
Constructor<?> ctor = clazz.getConstructor(String.class);
Object object = ctor.newInstance(new Object[] { ctorArgument });

or

Class<?> clazz = Class.forName(whichClass);
Object object = clazz.newInstance();

There is also the possibility to cast the Object to the corresponding Class if needed.

This would allow you to keep the mapping away from the code (simply defined in a configuration file) and be easily changed if needed.

The question Creating an instance using the class name and calling constructor might be an interesting read.

P.S.: I assumed Java for the examples but I guess this can also be done in other OO languages.

João Dias
  • 16,277
  • 6
  • 33
  • 45
  • Thanks, I am familiar with C++ and have never used Java so this is a bit alien to me. I will definitely research it and see if anything can be recreated in C++. – WalleyM Aug 25 '21 at 21:07
0

I have never knowingly used it, but the Prototype pattern might work for you.

Alternatively, use reflection* to see at runtime what type the first object is, and use that information to decide what type to build for object two.

  • Reflection is the name of a concept in .Net which I suspect will be have similar implementations in other languages.
Adrian K
  • 9,880
  • 3
  • 33
  • 59
  • Can you elaborate on how the Prototype pattern could be used? The way I see it is that it allows us to use a virtual copy constructor to recreate instances of one class. In this case it'd allow us to recreate object 1, but not create object 2. If we modify it to return an instance of object 2, we are essentially coupling the two classes, which is something I said in the question that is not possible in this instance. – WalleyM Aug 25 '21 at 07:44
  • That reflection idea is essentially the factory solution I gave in my answer. This is the only way I can currently see of solving this problem, however it has the shortcomings that I specified in the answer. – WalleyM Aug 25 '21 at 07:48