OOP is about programming with interface, not the implementation. Objects are "talking" to each other using their interfaces. When interfaces are not well defined one object can know too much about the other and if you need to change implementation of your object you'll need to change your programm in different places. That's really bad, it`s all about changing things in one place and DRY principles. So my question is the following: what if make interface context. Interface context will make sure that if object provides multiple interfaces other objects will only be able to use the one they need. This will ease things and reduce possibility of errors.
Let me introduce the example: some abstract class that let us read text and write text. So it has two interfaces: reader and writer. If you are dealing with reader, all you need is reader methods. So we can remove the writer interface. In the real world you'll still see the reader part (in IDEs like Visual Studio). But what if make such a thing that would allow you to declare "I'm in the reader context" and you`ll be able to do things only related to reading and every class will show you its reader related interface. What do you think? Does it make sense? Like add-ins to Visual Studio that will hide other interfaces when set to one?
Yes, that often makes sense, especially as you start to work more with Inversion of Control principles.
The easiest way to do this is to imlement the interface explicitly.
public interface IFoo
{
void Foo();
}
public interface IBar
{
void Bar();
}
public class Baz : IFoo, IBar
{
void IFoo.Foo()
{
}
void IBar.Bar()
{
}
}
public class Program
{
public void DoStuff()
{
Baz b = new Baz();
//b.Foo(); // doesn't compile
IFoo f = b as IFoo;
f.Foo(); // works
IBar bar = b as IBar;
bar.Bar(); //now we an see that.
}
}
The Foo method will only show up on Bar after you cast it to an IFoo.
Have a look around for Robert Martin's "Interface Segration Principle", as well as the rest of the SOLID principles of object oriented design.
In Eclipse, you have a method called getAdapter() which tries to turn an object into something else. So you can say:
Writer w = text.getAdapter(Writer.class);
Then the text can figure out how to turn itself into a Writer and create one for you. The next step is to have an adapter registry, so you can say:
AdapterRegistry.instance().register(TextDocument.class,
new TextDocumentAdapterFactory ());
In TextDocument, you can then say:
public Object getAdapter(Class c) {
AdapterRegistry.instance().createAdapter(this, c);
}
This way, you can add adapters for existing objects at a later time.
On the positive side, this makes your code extremely flexible. On the negative side, it makes the code impossible to debug since you can't tell what getAdapter() migth eventually return.
Related
I'd like to ask whether this is a useful concept, if other languages have ever done this sort of thing, or if this idea is problematic or just bad. If it is problematic, an explanation of what principles it violates would also be greatly appreciated.
For the sake of being clear about what I mean, I've written some C# pseudocode where I've created an imaginary "lazy" keyword that provides a concrete implementation of this idea. The "lazy" keyword instructs the compiler to 1) explicit cast any object that has functions that conform to an interface contract to that interface, even if the object in question does not explicitly implement the interface and 2) if said explicit cast function doesn't exist, create it, 3.) The object can be cast back to what it was originally, 4.) If the object doesn't implement the methods required by the interface, you get a compiler error.
Then the following code would compile and run.
class Program
{
public interface iRoll
{
public void Roll();
public int Dimensions { get; set;}
}
public class Basketball
{
public void Roll()
{
Console.WriteLine("I'm a rolling basketball");
}
private int _dimensions = 3;
public int Dimensions { get { return _dimensions; } set { _dimensions = value; } }
public string Brand = "BallCo.";
}
public class Tire
{
public void Roll()
{
Console.WriteLine("I'm a rolling tire");
}
private int _dimensions = 3;
public int Dimensions { get { return _dimensions; } set { _dimensions = value; } }
}
static void Main(string[] args)
{
Tire MyTire = new Tire();
Basketball MyBall = new Basketball();
var myList = new List<iRoll>();
myList.Add(lazy iRoll MyTire);
myList.Add(lazy iRoll MyBall);
foreach(iRoll myIRoll in myList)
{
myIRoll.Roll();
Console.WriteLine("My dimensions: " + myIRoll.Dimensions);
}
}
}
The benefits are not always having classes implement interfaces like crazy, and not having to derive from a base class just to implement a custom interface when the base class already has the methods and properties you need (e.g., certain situations with external libraries, certain UI controls).
Good idea, bad idea, terrible idea? Do any other languages experiment with this?
Thanks to all of you for the information. I found a similar question to my own with some interesting information. Two very important related and different concepts to learn about are structural typing and duck typing , both of which could fit my original question.
In my example, C# uses nominal typing which is not compatible with structural typing. The "lazy" keyword I proposed is a keyword that causes a nonimally-typed system to do certain things that make it look to a programmer like a structurally typed system. That should be static duck typing in a nominally typed language, for this example.
I wonder if someone could say the lazy keyword isn't "really" duck typing, but semantic sugar to have classes implement interfaces, if the implementation details of the lazy keyword caused the compiler to have the class operated on to implement any interfaces it needs to implement at compile time. However, I think duck typing is an OOP concept, so this should be duck typing regardless of what the compiler does as long as the end result acts like duck typing. Please feel free to correct anything I'm mistaken about or disagree.
There's a great section in the Wikipedia article about duck typing that shows many examples of it in programming languages.
I have a base class where all common functions are written. I many classes which override this functions by virtual keyword. Like,
public class Base
{
public virtual void sample()
{
..............
}
}
public class a : Base
{
public override sample()
{
}
}
public class implement
{
public void ToSample()
{
Base baseclass = new Base();
Switch(test)
{
case a: baseclass = a();
break;
case b: baseclass = b();
break;
}
baseclass.sample();
}
}
This perfect code for current situation but now I have more class to be assign in switch case. It is not good practice for adding huge amount of cases so I want something that automatically assign child class.
Is anybody know something to be implement ?
As stated in the comment, you can decouple the implementation by using dependency injection. Note however, that in some cases you have no choice but doing that kind of switch (e.g. when you need to create a class based on a text received in a socket). In such cases the important thing is to always keep the switch statement encapsulated in one method and make your objects rely on it (or, in other words, don't copy-and-paste it everywhere :)). The idea here is too keep your system isolated from a potentially harmful code. Of course that if you add a new class you will have to go and modify that method, however you will only have to do it in one time and in one specific place.
Another approach that I have seen (and sometimes used) is to build a mapping between values an classes. So, if your class-creation switch depends on an integer code, you basically create a mapping between codes and classes. What you are doing here is turning a "static" switch into a dynamic behavior, since you can change the mappings contents at any time and thus alter the way your program behaves. A typical implementation would be something like (sorry for the pseudocode, I'm not familiar with C#):
public class implement
{
public void ToSample()
{
class = this.mapping.valueForKey(test);
Base baseclass = new class();
baseclass.sample();
}
}
Note however that for this example to work you need reflection support, which varies according to the language you are using (again, sorry but I don't know the C# specifics).
Finally, you can also check the creational family of patterns for inspiration regarding object creation issues and some well known forms of solving them.
HTH
Is it possible to extend or modify the code of a C# class at runtime?
My question specifically revolves around Monkey Patching / Duck Punching or Meta Object Programming (MOP), as it happens in scripting languages such as Groovy, Ruby etc.
For those still stumbling on this question in the present day, there is indeed a present-day library called Harmony that relatively-straightforwardly enables such monkey-patching at runtime. Its focus is on video game modding (particularly games built with Unity), but there ain't much stopping folks from using it outside of that use case.
Copying the example from their introduction, if you have an existing class like so:
public class SomeGameClass
{
public bool isRunning;
public int counter;
private int DoSomething()
{
if (isRunning)
{
counter++;
}
return counter * 10;
}
}
Then Harmony can patch it like so:
using HarmonyLib;
using Intro_SomeGame;
public class MyPatcher
{
// make sure DoPatching() is called at start either by
// the mod loader or by your injector
public static void DoPatching()
{
var harmony = new Harmony("com.example.patch");
harmony.PatchAll();
}
}
[HarmonyPatch(typeof(SomeGameClass))]
[HarmonyPatch("DoSomething")]
class Patch01
{
static AccessTools.FieldRef<SomeGameClass, bool> isRunningRef =
AccessTools.FieldRefAccess<SomeGameClass, bool>("isRunning");
static bool Prefix(SomeGameClass __instance, ref int ___counter)
{
isRunningRef(__instance) = true;
if (___counter > 100)
return false;
___counter = 0;
return true;
}
static void Postfix(ref int __result)
{
__result *= 2;
}
}
Here, we have a "prefix" patch which gets inserted before the original method runs, allowing us to set variables within the method, set fields on the method's class, or even skip the original method entirely. We also have a "postfix" patch which gets inserted after the original method runs, and can manipulate things like the return value.
Obviously this ain't quite as nice as the sorts of monkey-patching you can do in e.g. Ruby, and there are a lot of caveats that might hinder its usefulness depending on your use case, but in those situations where you really do need to alter methods, Harmony's a pretty proven approach to doing so.
Is it possible to extend or modify the code of a C# class at run-time?
No it is not possible to do this in .NET. You could write derived classes and override methods (if they are virtual) but you cannot modify an existing class. Just imagine if what you were asking was possible: you could modify the behavior of some existing system classes like System.String.
You may also take a look at Extension methods to add functionality to an existing class.
You can add functionality, but you cannot change or remove functionality.
You can extend classes by adding extra methods, but you cannot override them because added methods have always lower priority than existing ones.
For more info, check Extension Methods in C# Programming Guide.
A common red flag that an OOP language is not being leveraged properly looks like this:
if (typeof(x) == T1)
{
DoSomethingWithT1(x);
}
else if (typeof(x) == T2)
{
DoSomethingWithT2(x);
}
The standard "fix" for such design issues is to make T1 and T2 both share an interface, either through inheritance of a base type or implementation of a common interface (in languages that support it); for example, in C# a solution might be:
public interface IT
{
void DoSomething();
}
However, sometimes you want to implement functionality that differs based on the type of an object but that functionality does not belong within that object's type; thus polymorphism seems the wrong way to go.
For example, consider the case of a UI that provides a view of a given clump of data. Supposing this view is capable of rendering various layouts and controls depending on the type of data being presented, how would you implement this type-specific rendering without a bunch of if/else statements?
For reasons that I hope are obvious, putting the rendering logic in the type itself strikes me as a very bad decision in this case. On the other hand, without coupling the type of data object to its visual presentation I have a hard time seeing how the if/else scenario is avoided.
Here's a concrete example: I work on a trading application which utilizes many different pricing models for various market products. These different models are represented by types inheriting from a common PricingModel base; and each type is associated with a completely different set of parameters. When the user wants to view the parameters for a particular pricing model (for a particular product), currently these are displayed by a form which detects the type of the model and displays an appropriate set of controls. My question is how this could be implemented more elegantly than it is currently (with a big if/else block).
I realize this probably seems like a very basic question; it's just one of those gaps in my knowledge (of solid OOP principles? design patterns? common sense?) that I figured it's about time to fix.
We are injecting (Spring.Net) such functionality into dictionaries by type.
IDictionary<Type, IBlahImplementor> blahImplementors;
blahImplementors[thingy.GetType()].Do(thingy);
This dictionary could be managed by a kind of repository which provides the functionality.
As an implementation detail, the implementor usually knows the type it depends on an can provide it itself:
interface IBlahImplementor
{
Type ForType { get; }
void Do(object thingy);
}
Then it is added to the dictionary like this:
IEnumerably<IBlahImplementor> blahImplementors;
foreach (var implementor in blahImplementors)
{
blahImplementors.Add(implementor.ForType, implementor);
}
Remark: IMHO, it is very important to understand that some things do NOT belong into a class, even if providing subtype-specific implementations would make life much easier.
Edit: Finally understood your concrete example.
It is actually about instancing the right UI control to show the pricing models parameters. It should be possible with the pattern I described above. If you don't have a single UI control for a pricing model, you either create it or you write a UI configurer or something like this which sets up the required controls.
interface IPricingModelUiConfigurer
{
Type PricingModelType { get; }
void SetupUi(Control parent, IPricingModel model);
}
you can use common interface approach as you describe and Command pattern to trigger methods with "functionality does not belong within that object's type". I think this won't break solid OOP principles.
What you described is pretty much exactly the use case for the Visitor Pattern.
EDIT: For your concrete example, you could apply the visitor pattern like this:
// interface used to add external functionality to pricing models
public interface PricingModelVisitor {
void visitPricingModel1(PricingModel1 m);
void visitPricingModel2(PricingModel2 m);
...
}
// your existing base-class, with added abstract accept() method to accept a visitor
public abstract class PricingModelBase {
public abstract void accept(PricingModelVisitor v);
...
}
// concrete implementations of the PricingModelBase implement accept() by calling the
// appropriate method on the visitor, passing themselves as the argument
public class PricingModel1 : PricingModelBase {
public void accept(PricingModelVisitor v) { v.visitPricingModel1(this); }
...
}
public class PricingModel2 : PricingModel {
public void accept(PricingModelVisitor v) { v.visitPricingModel2(this); }
...
}
// concrete implementation of the visitor interface, in this case with the new
// functionality of adding the appropriate controls to a parent control
public class ParameterGuiVisitor : PricingModelVisitor {
private Control _parent;
public ParameterGuiVisitor(Control parent) { _parent = parent; }
visitPricingModel1(PricingModel1 m) {
// add controls to _parent for PricingModel1
}
visitPricingModel2(PricingModel2 m) {
// add controls to _parent for PricingModel1
}
}
now, instead of using a big if-else block when you need to display the edit-controls for the parameters of a specific subtype of PricingModelVisitor, you can simply call
somePricingModel.accept(new ParameterGuiVisitor(parentControl))
and it will populate the appropriate GUI for you.
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!