How to architect an embedded system with multiple input and output capabilities. Some based on hardware, some on software settings - oop

I have an ESP8266 project programmed in the Arduino framework that gathers data from the network and then displays on a display. The device can be built with a few different display hardware types (eink, led, oled). These are set at compile time with #defines. However there are also a few different type of data and data transport mechanisms that can be used. Some require hardware (LoRa TX/RX) and are enabled at compile time but some can be changed at runtime based on user settings (eg. HTTP or MQTT).
I'm already using a factory design pattern to instantiate the Data transport object at runtime but still use compile time build flags to select which display hardware to use. I have a Display class, a Datasource class and a Config class. This has worked well but is now reaching its limit as I try to add Cellular functionality.
I wonder if there is a good design pattern / architecture design that will facilitate this kind of flexibility without having to keep adding more and more intrusive #ifdef statements all over my code.
Attached is a little mind map of the basic layout of possibilities of this device.

If you want to make a decision what algorithn should be injected at runtime, then you can try to use Strategy pattern.
As wiki says about strategy pattern:
In computer programming, the strategy pattern (also known as the
policy pattern) is a behavioral software design pattern that enables
selecting an algorithm at runtime. Instead of implementing a single
algorithm directly, code receives run-time instructions as to which in
a family of algorithms to use
So you can read your config file and choose what object should be instantiated. For example, you have many displays:
public enum DisplayMark
{
Samsung, Sony, Dell
}
and then yoy should create a base class Display:
public abstract class Display
{
public abstract string Show();
}
And then you need concrete implementations of this base class Display:
public class SamsungDisplay : Display
{
public override string Show()
{
return "I am Samsung";
}
}
public abstract class SonyDisplay : Display
{
public override string Show()
{
return "I am Sony";
}
}
public abstract class DellDisplay : Display
{
public override string Show()
{
return "I am Dell";
}
}
So far, so good. Now we need something like mapper which will be responsible to bring correct instance by selected display from config:
public class DisplayFactory
{
public Dictionary<DisplayMark, Display> DisplayByMark { get; private set; }
= new Dictionary<DisplayMark, Display>
{
{ DisplayMark.Sony, new SonyDisplay()},
{ DisplayMark.Samsung, new SamsungDisplay()},
{ DisplayMark.Dell, new DellDisplay()},
};
}
and then when you will know what display should be used from config file, then you can get desired instance:
public void UseDisplay(DisplayMark displayMark)
{
DisplayFactory displayFactory = new DisplayFactory();
Display display = displayFactory.DisplayByMark[displayMark];
// Here you can use your desired display class
display.Show();
}

Related

Implementing multiple overloaded functions vs doing inside checking/validation for enforcing business logic

This is a general issue, but i will demonstrate it using the following problem:
I want to model airCrafts. now, for every aircraft there is one capability that is loaded with: attackCapability, IntelligenceCapability and BdaCapability. Not all aircraft can be loaded with all capabilities - every airCraft has its own potential capabilities that is supports and can be loaded with.
I want to implement this business logic with composition:
every aircraft object will hold a private member of type Capability (every capability will be implemented by a class that inherits from this abstract class/interface).
Now - I want to design a code that will enforce the business logic - i.e. will not allow any programmer to load an aircraft with an ability it doesn't support.
I have two options for doing this:
implement several overloaded version of the SetCapability() function - each one with the type of capability i want to support. For example:
public void SetCapability(AttackCapability capability);
public void SetCapability(BdaCapability capability);
That way the user can't load the aircraft with forbidden capability - and if he/she will try doing so, there will be a compilation erorr - i.e. that IDE will show some error message.
implement one function: public void SetCapability(Capability capability)
and doing some checking inside it. something like this:
public void SetCapability(Capability capability){
if(capability.getType() != typeOf(AttackCapability) || capability.getType() != typeOf(BdaCapability){
throw new InvalidOperationException();
}
_capability = capability;
}
the disdvantage here is that misuse of the user will be identified only at runtime instead at compiletime (much harder to identify and more bug prone), but as opposed to the previous option - it doesn't make you write several overloaded function which may cause the class to look heavy, strange and less readable for the inexperienced programmer.
(code reusability is not a big issues, because we always can implement private function like: private void SetCapabiltiy(Capability capability); which do the setting logic and every public overloaded SetCapability function will call it).
I feel that both option have their advantages and disadvantages as i described above.
I tend to prefer the first one, because it is more safe and hermeric - but it frequently causes my code to look "ugly" and some programmers may interprete it as duplicated code and don't understand the point...
I would like to hear your opinion for this issues, and maybe you have some better designs for this problem.
I couldn't understand your #1 option, but I think in any option you said you need to check permitted capabilities statically. This would result to change the code in future and would break the OCP. Instead of this I thought that maybe we can take advantage of dynamic dispatch here and let it to check types.
IMHO you can create final concrete classes and set required access modifiers to private in the concretes and then use factories(actually the abstract factory pattern looks suitable here) to hide object creation from clients to enforce business logic and the use the technique(which is my term) that referencing the same type used in Decorator or Chain of Responsibility patterns to keep capabilities in a chain by which you may have extra possibilities to dynamically check the capabilities to apply the behavior they require instead of just in a list( which could be used with Command pattern also)
As a note, the section where you mention as your second option limits the extensibility and generally manual type check is considered as bad practice in a dynamically dispatched or duck typed OOP language runtime. I know the fact that you are checking the field not a type but this is also a manual control and capability as the name implies is a behavior not state.
Finally since the aircrafts don't have same functionalities, but varying behaviors Visitor pattern could be used to design functionalities by which you create method classes instead of classes containing methods.
In addition, a Map<Aircraft, List<Capability>> could be used by keeping in a config object chek the features when creating objects by using DI.
//Capability types
interface Capable {
do();
}
class Attacker implements Capable {
private Capable capability;
public Attacker(Capable capability) { //to keep track of next/linked capability
this.capability = capability;
}
public do(String request) {
this.attack();
this.capability.do();//or instead of this decorator you could use chain of responsibility with next() and handle() below.
}
//to select features in some functionality.
public handle(String request) {
if ("attack".equals(request)) { //just to show what you can by keeping a reference to same type
this.attack();
} else {
this.capability.do();
}
}
public next(Capable capability) {
this.capability = capability;
}
}
class Intelligent implements Capable {
//similar to above.
}
//Aircraft types
class F111 implements Aircraft {
private Capable capability;
//or store capabilities in a list and check with config mapper object(maps aircrafts with its capabilities in a Map<Aircraft.TYPE, List<Capable> capabilities)
//private List<Capable> capabilities;
//other state
Aircraft(Capable capability) { //or create a factory
this.capability = capability;
}
//behaviors
doSth() {
this.capability.do();
}
}
class F222 implements Aircraft {
//...
}
//To hide creation of requested aircraft with only its required capabilities from the client
static class AircraftFactory { //dont permit to directly access to Aircraft concretes
static Aircraft getAircraft(String type) {//could be used abstract factory instead of this.
if("f111".equals(type)) {
return new F111(new Attacker(new Intelligent()));
}
else if(...) { new F222(new Intelligent(new Bda())}
else if(...) { new F001(new Default()) }
}
}
class Client {
main() {
//instead of this
//Aircraft f9999 = new Aircraft(new Attacker);
//f9999.doSth();
//enforce client to use factory.
Aircraft aircraft = AircraftFactory.getAircraft("f222");
aircraft.doSth();
}
}

JavaFX Wrap an Existing Object with Simple Properties

I am writing a new app and I have chosen to use Java for flexibility. It is a GUI app so I will use JavaFX. This is my first time using Java but I have experience with C#.
I am getting familiar with JavaFX Properties, they look like a great way of bi-directional binding between front-end and back-end.
My code uses classes from an open-source API, and I would like to convert the members of these classes to JavaFX Properties (String => StringProperty, etc). I believe this would be transparent to any objects that refer to these members.
Is it ok to do this?
Is it the suggested way of dealing with existing classes?
What do I do about Enum types? E.g. an enum member has it's value changed, how should I connect the enum member to the front-end?
Thank you :)
In general, as long as you don't change the public API of the class - in other words you don't remove any public methods, modify their parameter types or return types, or change their functionality - you should not break any code that uses them.
So, e.g. a change from
public class Foo {
private String bar ;
public String getBar() {
return bar ;
}
public void setBar(String bar) {
this.bar = bar ;
}
}
to
public class Foo {
private final StringProperty bar = new SimpleStringProperty();
public StringProperty barProperty() {
return bar ;
}
public String getBar() {
return barProperty().get();
}
public void setBar(String bar) {
barProperty().set(bar);
}
}
should not break any clients of the class Foo. The only possible problem is that classes that have subclassed Foo and overridden getBar() and/or setBar(...) might get unexpected behavior if their superclass is replaced with the new implementation (specifically, if getBar() and setBar(...) are not final, you have no way to enforce that getBar()==barProperty().get(), which is desirable).
For enums (and other objects) you can use an ObjectProperty<>:
Given
public enum Option { FIRST_CHOICE, SECOND_CHOICE, THIRD_CHOICE }
Then you can do
public class Foo {
private final ObjectProperty<Option> option = new SimpleObjectProperty<>();
public ObjectProperty<Option> optionProperty() {
return option ;
}
public Option getOption() {
return optionProperty().get();
}
public void setOption(Option choice) {
optionProperty().set(choice);
}
}
One caveat to all this is that you do introduce a dependency on the JavaFX API that wasn't previously present in these classes. JavaFX ships with the Oracle JDK, but it is not a full part of the JSE (e.g. it is not included in OpenJDK by default, and not included in some other JSE implementations). So in practice, you're highly unlikely to be able to persuade the developers of the open source library to accept your changes to the classes in the library. Since it's open source, you can of course maintain your own fork of the library with JavaFX properties, but then it will get tricky if you want to incorporate new versions of that library (you will need to merge two different sets of changes, essentially).
Another option is to use bound properties in the classes, and wrap them using a Java Bean Property Adapter. This is described in this question.

When is an "interface" useful?

OOP interfaces.
In my own experience I find interfaces very useful when it comes to design and implement multiple inter-operating modules with multiple developers. For example, if there are two developers, one working on backend and other on frontend (UI) then they can start working in parallel once they have interfaces finalized. Thus, if everyone follows the defined contract then the integration later becomes painless. And thats what interfaces precisely do - define the contract!
Basically it avoids this situation :
Interfaces are very useful when you need a class to operate on generic methods implemented by subclasses.
public class Person
{
public void Eat(IFruit fruit)
{
Console.WriteLine("The {0} is delicious!",fruit.Name);
}
}
public interface IFruit
{
string Name { get; }
}
public class Apple : IFruit
{
public string Name
{
get { return "Apple"; }
}
}
public class Strawberry : IFruit
{
public string Name
{
get { return "Strawberry"; }
}
}
Interfaces are very useful, in case of multiple inheritance.
An Interface totally abstracts away the implementation knowledge from the client.
It allows us to change their behavior dynamically. This means how it will act depends on dynamic specialization (or substitution).
It prevents the client from being broken if the developer made some changes
to implementation or added new specialization/implementation.
It gives an open way to extend an implementation.
Programming language (C#, java )
These languages do not support multiple inheritance from classes, however, they do support multiple inheritance from interfaces; this is yet another advantage of an interface.
Basically Interfaces allow a Program to change the Implementation without having to tell all clients that they now need a "Bar" Object instead of a "Foo" Object. It tells the users of this class what it does, not what it is.
Example:
A Method you wrote wants to loop through the values given to it. Now there are several things you can iterate over, like Lists, Arrays and Collections.
Without Interfaces you would have to write:
public class Foo<T>
{
public void DoSomething(T items[])
{
}
public void DoSomething(List<T> items)
{
}
public void DoSomething(SomeCollectionType<T> items)
{
}
}
And for every new iteratable type you'd have to add another method or the user of your class would have to cast his data. For example with this solution if he has a Collection of FooCollectionType he has to cast it to an Array, List or SomeOtherCollectionType.
With interfaces you only need:
public class Foo<T>
{
public void DoSomething(IEnumerable<T> items)
{
}
}
This means your class only has to know that, whatever the user passes to it can be iterated over. If the user changes his SomeCollectionType to AnotherCollectionType he neither has to cast nor change your class.
Take note that abstract base classes allow for the same sort of abstraction but have some slight differences in usage.

Architecture of some reusable code

I am writing a number of small, simple applications which share a common structure and need to do some of the same things in the same ways (e.g. logging, database connection setup, environment setup) and I'm looking for some advice in structuring the reusable components. The code is written in a strongly and statically typed language (e.g. Java or C#, I've had to solve this problem in both). At the moment I've got this:
abstract class EmptyApp //this is the reusable bit
{
//various useful fields: loggers, db connections
abstract function body()
function run()
{
//do setup
this.body()
//do cleanup
}
}
class theApp extends EmptyApp //this is a given app
{
function body()
{
//do stuff using some fields from EmptyApp
}
function main()
{
theApp app = new theApp()
app.run()
}
}
Is there a better way? Perhaps as follows? I'm having trouble weighing the trade-offs...
abstract class EmptyApp
{
//various fields
}
class ReusableBits
{
static function doSetup(EmptyApp theApp)
static function doCleanup(EmptyApp theApp)
}
class theApp extends EmptyApp
{
function main()
{
ReusableBits.doSetup(this);
//do stuff using some fields from EmptyApp
ReusableBits.doCleanup(this);
}
}
One obvious tradeoff is that with option 2, the 'framework' can't wrap the app in a try-catch block...
I've always favored re-use through composition (your second option) rather than inheritance (your first option).
Inheritance should only be used when there is a relationship between the classes rather than for code reuse.
So for your example I would have multiple ReusableBits classes each doing 1 thing that each application a make use of as/when required.
This allows each application to re-use the parts of your framework that are relevant for that specific application without being forced to take everything, Allowing the individual applications more freedom. Re-use through inheritance can sometimes become very restrictive if you have some applications in the future that don't exactly fit into the structure you have in mind today.
You will also find unit testing and test driven development much easier if you break your framework up into separate utilities.
Why not make the framework call onto your customisable code ? So your client creates some object, and injects it into the framework. The framework initialises, calls setup() etc., and then calls your client's code. Upon completion (or even after a thrown exception), the framework then calls cleanup() and exits.
So your client would simply implement an interface such as (in Java)
public interface ClientCode {
void runClientStuff(); // for the sake of argument
}
and the framework code is configured with an implementation of this, and calls runClientStuff() whenever required.
So you don't derive from the application framework, but simply provide a class conforming to a particular contract. You can configure the application setup at runtime (e.g. what class the client will provide to the app) since you're not deriving from the app and so your dependency isn't static.
The above interface can be extended to have multiple methods, and the application can call the required methods at different stages in the lifecycle (e.g. to provide client-specific setup/cleanup) but that's an example of feature creep :-)
Remember, inheritance is only a good choice if all the object that are inheriting reuse the code duo to their similarities. or if you want callers to be able to interact with them in the same fission.
if what i just mentioned applies to you then based on my experience its always better to have the common logic in your base/abstract class.
this is how i would re-write your sample app in C#.
abstract class BaseClass
{
string field1 = "Hello World";
string field2 = "Goodbye World";
public void Start()
{
Console.WriteLine("Starting.");
Setup();
CustomWork();
Cleanup();
}
public virtual void Setup()
{Console.WriteLine("Doing Base Setup.");}
public virtual void Cleanup()
{Console.WriteLine("Doing Base Cleanup.");}
public abstract void CustomWork();
}
class MyClass : BaseClass
{
public override void CustomWork()
{Console.WriteLine("Doing Custome work.");}
public override void Cleanup()
{
Console.WriteLine("Doing Custom Cleanup");
//You can skip the next line if you want to replace the
//cleanup code rather than extending it
base.Cleanup();
}
}
void Main()
{
MyClass worker = new MyClass();
worker.Start();
}

Alternative to the visitor pattern?

I am looking for an alternative to the visitor pattern. Let me just focus on a couple of pertinent aspects of the pattern, while skipping over unimportant details. I'll use a Shape example (sorry!):
You have a hierarchy of objects that implement the IShape interface
You have a number of global operations that are to be performed on all objects in the hierarchy, e.g. Draw, WriteToXml etc...
It is tempting to dive straight in and add a Draw() and WriteToXml() method to the IShape interface. This is not necessarily a good thing - whenever you wish to add a new operation that is to be performed on all shapes, each IShape-derived class must be changed
Implementing a visitor for each operation i.e. a Draw visitor or a WirteToXml visitor encapsulates all the code for that operation in one class. Adding a new operation is then a matter of creating a new visitor class that performs the operation on all types of IShape
When you need to add a new IShape-derived class, you essentially have the same problem as you did in 3 - all visitor classes must be changed to add a method to handle the new IShape-derived type
Most places where you read about the visitor pattern state that point 5 is pretty much the main criteria for the pattern to work and I totally agree. If the number of IShape-derived classes is fixed, then this can be a quite elegant approach.
So, the problem is when a new IShape-derived class is added - each visitor implementation needs to add a new method to handle that class. This is, at best, unpleasant and, at worst, not possible and shows that this pattern is not really designed to cope with such changes.
So, the question is has anybody come across alterative approaches to handling this situation?
You might want to have a look at the Strategy pattern. This still gives you a separation of concerns while still being able to add new functionality without having to change each class in your hierarchy.
class AbstractShape
{
IXmlWriter _xmlWriter = null;
IShapeDrawer _shapeDrawer = null;
public AbstractShape(IXmlWriter xmlWriter,
IShapeDrawer drawer)
{
_xmlWriter = xmlWriter;
_shapeDrawer = drawer;
}
//...
public void WriteToXml(IStream stream)
{
_xmlWriter.Write(this, stream);
}
public void Draw()
{
_drawer.Draw(this);
}
// any operation could easily be injected and executed
// on this object at run-time
public void Execute(IGeneralStrategy generalOperation)
{
generalOperation.Execute(this);
}
}
More information is in this related discussion:
Should an object write itself out to a file, or should another object act on it to perform I/O?
There is the "Visitor Pattern With Default", in which you do the visitor pattern as normal but then define an abstract class that implements your IShapeVisitor class by delegating everything to an abstract method with the signature visitDefault(IShape).
Then, when you define a visitor, extend this abstract class instead of implementing the interface directly. You can override the visit* methods you know about at that time, and provide for a sensible default. However, if there really isn't any way to figure out sensible default behavior ahead of time, you should just implement the interface directly.
When you add a new IShape subclass, then, you fix the abstract class to delegate to its visitDefault method, and every visitor that specified a default behavior gets that behavior for the new IShape.
A variation on this if your IShape classes fall naturally into a hierarchy is to make the abstract class delegate through several different methods; for example, an DefaultAnimalVisitor might do:
public abstract class DefaultAnimalVisitor implements IAnimalVisitor {
// The concrete animal classes we have so far: Lion, Tiger, Bear, Snake
public void visitLion(Lion l) { visitFeline(l); }
public void visitTiger(Tiger t) { visitFeline(t); }
public void visitBear(Bear b) { visitMammal(b); }
public void visitSnake(Snake s) { visitDefault(s); }
// Up the class hierarchy
public void visitFeline(Feline f) { visitMammal(f); }
public void visitMammal(Mammal m) { visitDefault(m); }
public abstract void visitDefault(Animal a);
}
This lets you define visitors that specify their behavior at whatever level of specificity you wish.
Unfortunately, there is no way to avoid doing something to specify how visitors will behave with a new class - either you can set up a default ahead of time, or you can't. (See also the second panel of this cartoon )
I maintain a CAD/CAM software for metal cutting machine. So I have some experience with this issues.
When we first converted our software (it was first released in 1985!) to a object oriented designed I did just what you don't like. Objects and Interfaces had Draw, WriteToFile, etc. Discovering and reading about Design Patterns midway through the conversion helped a lot but there were still a lot of bad code smells.
Eventually I realized that none of these types of operations were really the concern of the object. But rather the various subsystems that needed to do the various operations. I handled this by using what is now called a Passive View Command object, and well defined Interface between the layers of software.
Our software is structured basically like this
The Forms implementing various Form
Interface. These forms are a thing shell passing events to the UI Layer.
UI layer that receives Events and manipulate forms through the Form interface.
The UI Layer will execute commands that all implement the Command interface
The UI Object have interfaces of their own that the command can interact with.
The Commands get the information they need, process it, manipulates the model and then report back to the UI Objects which then does anything needed with the forms.
Finally the models which contains the various objects of our system. Like Shape Programs, Cutting Paths, Cutting Table, and Metal Sheets.
So Drawing is handled in the UI Layer. We have different software for different machines. So while all of our software share the same model and reuse many of the same commands. They handle things like drawing very different. For a example a cutting table is draw different for a router machine versus a machine using a plasma torch despite them both being esstentially a giant X-Y flat table. This because like cars the two machines are built differently enough so that there is a visual difference to the customer.
As for shapes what we do is as follows
We have shape programs that produce cutting paths through the entered parameters. The cutting path knows which shape program produced. However a cutting path isn't a shape. It just the information needed to draw on the screen and to cut the shape. One reason for this design is that cutting paths can be created without a shape program when they are imported from a external app.
This design allows us to separate the design of the cutting path from the design of the shape which are not always the same thing. In your case likely all you need to package is the information needed to draw the shape.
Each shape program has a number of views implementing a IShapeView Interface. Through the IShapeView interface the shape program can tell the generic shape form we have how to setup itself up to show the parameters of that shape. The generic shape form implements a IShapeForm interface and registers itself with the ShapeScreen Object. The ShapeScreen Object registers itself with our application object. The shape views use whatever shapescreen that registers itself with the application.
The reason for the multiple views that we have customers that like to enter shapes in different ways. Our customer base is split in half between those who like to enter shape parameters in a table form and those who like to enter with a graphical representation of the shape in front of them. We also need to access the parameters at times through a minimal dialog rather than our full shape entry screen. Hence the multiple views.
Commands that manipulate shapes fall in one of two catagories. Either they manipulate the cutting path or they manipulate the shape parameters. To manipulate the shape parameters generally we either throw them back into the shape entry screen or show the minimal dialog. Recalculate the shape, and display it in the same location.
For the cutting path we bundled up each operation in a separate command object. For example we have command objects
ResizePath
RotatePath
MovePath
SplitPath
and so on.
When we need to add new functionality we add another command object, find a menu, keyboard short or toolbar button slot in the right UI screen and setup the UI object to ececute that command.
For example
CuttingTableScreen.KeyRoute.Add vbShift+vbKeyF1, New MirrorPath
or
CuttingTableScreen.Toolbar("Edit Path").AddButton Application.Icons("MirrorPath"),"Mirror Path", New MirrorPath
In both instances the Command object MirrorPath is being associated with a desired UI element. In the execute method of MirrorPath is all the code needed to mirror the path in a particular axis. Likely the command will have it's own dialog or use one of the UI elements to ask the user which axis to mirror. None of this is making a visitor, or adding a method to the path.
You will find that a lot can be handled through bundling actions into commands. However I caution that is not a black or white situation. You will still find that certain things work better as methods on the original object. In may experience I found that perhaps 80% of what I used to do in methods were able to be moved into the command. The last 20% just plain work better on the object.
Now some may not like this because it seems to violate encapsulations. From maintaining our software as a object oriented system for the last decade I have to say the MOST important long term thing you can do is clearly document the interactions between the different layers of your software and between the different objects.
Bundling actions into Command objects helps with this goal way better than a slavish devotion to the ideals of encapsulation. Everything that is needs to be done to Mirror a Path is bundled in the Mirror Path Command Object.
Visitor design pattern is a workaround, not a solution to the problem. Short answer would be pattern matching.
Regardless of what path you take, the implementation of alternate functionality that is currently provided by the Visitor pattern will have to 'know' something about the concrete implementation of the interface that it is working on. So there is no getting around the fact that you are going to have to write addition 'visitor' functionality for each additional implementation. That said what you are looking for is a more flexible and structured approach to creating this functionality.
You need to separate out the visitor functionality from the interface of the shape.
What I would propose is a creationist approach via an abstract factory to create replacement implementations for visitor functionality.
public interface IShape {
// .. common shape interfaces
}
//
// This is an interface of a factory product that performs 'work' on the shape.
//
public interface IShapeWorker {
void process(IShape shape);
}
//
// This is the abstract factory that caters for all implementations of
// shape.
//
public interface IShapeWorkerFactory {
IShapeWorker build(IShape shape);
...
}
//
// In order to assemble a correct worker we need to create
// and implementation of the factory that links the Class of
// shape to an IShapeWorker implementation.
// To do this we implement an abstract class that implements IShapeWorkerFactory
//
public AbsractWorkerFactory implements IShapeWorkerFactory {
protected Hashtable map_ = null;
protected AbstractWorkerFactory() {
map_ = new Hashtable();
CreateWorkerMappings();
}
protected void AddMapping(Class c, IShapeWorker worker) {
map_.put(c, worker);
}
//
// Implement this method to add IShape implementations to IShapeWorker
// implementations.
//
protected abstract void CreateWorkerMappings();
public IShapeWorker build(IShape shape) {
return (IShapeWorker)map_.get(shape.getClass())
}
}
//
// An implementation that draws circles on graphics
//
public GraphicsCircleWorker implements IShapeWorker {
Graphics graphics_ = null;
public GraphicsCircleWorker(Graphics g) {
graphics_ = g;
}
public void process(IShape s) {
Circle circle = (Circle)s;
if( circle != null) {
// do something with it.
graphics_.doSomething();
}
}
}
//
// To replace the previous graphics visitor you create
// a GraphicsWorkderFactory that implements AbstractShapeFactory
// Adding mappings for those implementations of IShape that you are interested in.
//
public class GraphicsWorkerFactory implements AbstractShapeFactory {
Graphics graphics_ = null;
public GraphicsWorkerFactory(Graphics g) {
graphics_ = g;
}
protected void CreateWorkerMappings() {
AddMapping(Circle.class, new GraphicCircleWorker(graphics_));
}
}
//
// Now in your code you could do the following.
//
IShapeWorkerFactory factory = SelectAppropriateFactory();
//
// for each IShape in the heirarchy
//
for(IShape shape : shapeTreeFlattened) {
IShapeWorker worker = factory.build(shape);
if(worker != null)
worker.process(shape);
}
It still means that you have to write concrete implementations to work on new versions of 'shape' but because it is completely separated from the interface of shape, you can retrofit this solution without breaking the original interface and software that interacts with it. It acts as a sort of scaffolding around the implementations of IShape.
If you're using Java: Yes, it's called instanceof. People are overly scared to use it. Compared to the visitor pattern, it's generally faster, more straightforward, and not plagued by point #5.
If you have n IShapes and m operations that behave differently for each shape, then you require n*m individual functions. Putting these all in the same class seems like a terrible idea to me, giving you some sort of God object. So they should be grouped either by IShape, by putting m functions, one for each operation, in the IShape interface, or grouped by operation (by using the visitor pattern), by putting n functions, one for each IShape in each operation/visitor class.
You either have to update multiple classes when you add a new IShape or when you add a new operation, there is no way around it.
If you are looking for each operation to implement a default IShape function, then that would solve your problem, as in Daniel Martin's answer: https://stackoverflow.com/a/986034/1969638, although I would probably use overloading:
interface IVisitor
{
void visit(IShape shape);
void visit(Rectangle shape);
void visit(Circle shape);
}
interface IShape
{
//...
void accept(IVisitor visitor);
}
I have actually solved this problem using the following pattern. I do not know if it has a name or not!
public interface IShape
{
}
public interface ICircleShape : IShape
{
}
public interface ILineShape : IShape
{
}
public interface IShapeDrawer
{
void Draw(IShape shape);
/// <summary>
/// Returns the type of the shape this drawer is able to draw!
/// </summary>
Type SourceType { get; }
}
public sealed class LineShapeDrawer : IShapeDrawer
{
public Type SourceType => typeof(ILineShape);
public void Draw(IShape drawing)
{
if (drawing is ILineShape)
{
// Code to draw the line
}
}
}
public sealed class CircleShapeDrawer : IShapeDrawer
{
public Type SourceType => typeof(ICircleShape);
public void Draw(IShape drawing)
{
if (drawing is ICircleShape)
{
// Code to draw the circle
}
}
}
public sealed class ShapeDrawingClient
{
private readonly IDictionary<Type, IShapeDrawer> m_shapeDrawers =
new Dictionary<Type, IShapeDrawer>();
public void Add(IShapeDrawer shapeDrawer)
{
m_shapeDrawers[shapeDrawer.SourceType] = shapeDrawer;
}
public void Draw(IShape shape)
{
Type[] interfaces = shape.GetType().GetInterfaces();
foreach (Type #interface in interfaces)
{
if (m_shapeDrawers.TryGetValue(#interface, out IShapeDrawer drawer))
{
drawer.Draw(drawing);
return;
}
}
}
}
Usage:
LineShapeDrawer lineShapeDrawer = new LineShapeDrawer();
CircleShapeDrawer circleShapeDrawer = new CircleShapeDrawer();
ShapeDrawingClient client = new ShapeDrawingClient ();
client.Add(lineShapeDrawer);
client.Add(circleShapeDrawer);
foreach (IShape shape in shapes)
{
client.Draw(shape);
}
Now if someone as the user of my library defines IRectangleShape and wants to draw it, they can simply define IRectangleShapeDrawer and add it to ShapeDrawingClient's list of drawers!