4

A little background story on this question:

I'm writing a POS system for my local bar, which I would like to be plugin-enabled. For this particular problem I'll use the following scenario:

I've got a calculator class which, as the name suggests, does all the calculations involving products on the current receipt, VAT, etc. I want this class to be extendable by a plugin (in a different assembly) to add a functionality to use these special coins we use. The calculator simply loops through all the products on the receipt (which is passed in it's constructor) to calculate the total amount. The receipt class has a list of all the product objects that are on it. The built-in product class does not have any members related to coins, these are in another product class in the plugin assembly.

The question herein is, how do I make my main program dynamically select the correct class to use for products?

I was thinking of loading all the plugins into a list or dictionary and simply loop through them to check if an override for the product class exists and then use that, but I would still need some kind of common interface, which is not an option. I know exactly how to do it in PHP, but there one can edit/replace the source with an updated one at run/install time.

Thanks in advance, Thom.

EDIT: To simplify the problem: Is it possible to have an external plugin assembly extend a built-in assembly with new methods, without having to hard-code the method's names/delegates/whatever in the existing application?

FuST
  • 76
  • 1
  • 5
  • 14
    why having a common interface "is not an option"? – Tigran Apr 16 '13 at 15:09
  • 4
    Just as a piece of advice, you might want to reinvestigate the logic and workflow of what you are trying to do – Icemanind Apr 16 '13 at 15:13
  • http://stackoverflow.com/questions/4473010/plugin-function-implementation-with-c-sharp Could probably be helpful – Charles380 Apr 16 '13 at 15:17
  • A common interface is not an option because the plugin assembly has to add extra methods to the class. These methods are not known during compile time, so reflection seems the way to go. However, that simply is not dynamic enough. – FuST Apr 18 '13 at 14:35

4 Answers4

7

Don't see any reason, from the question provided, to not use a common interface. Example:

public interface IProduct {

      decimal Price {get;}
}

public class Product : IProduct 
{
     decimal price;
     public decimal Price {
          get {
              return price;
          }
     }
}

public class Product1 : IProduct 
{
     decimal price;
     public decimal Price {
          get {
              return price;
          }
     }
}
....

After Calculator iterates over the collection of IProducts and call for everyone its Price property, apply region related VAT + whatever and constructs final price.

Dirk
  • 10,668
  • 2
  • 35
  • 49
Tigran
  • 61,654
  • 8
  • 86
  • 123
  • Thanks for the answer. However, this is the current implementation and that's not what I'm looking for. The plugin assembly has to add a method (something like GetPriceCoins) to the existing built-in class. – FuST Apr 18 '13 at 14:32
  • @Thom: in these kind of architectures there is always a point where you need to separate. You can not have common interface all along workflow. The point is: insert in common base class/interface as much methods as possible that are shared between different plugins. The rest can be configured to be invoked via reflection (example), so avoid casting. – Tigran Apr 18 '13 at 14:52
  • 1
    Ok, think I got it. I've been thinking of a different approach to achieve what I want using observers. I'll try the above and my own solution and report back with the solution that worked so maybe someone else can use it. Thanks for the input so far! – FuST Apr 18 '13 at 14:59
2

MEF or any half decent DI container should be able to do that.. So all classes implement interface then you call RESOLVE that returns an enumerable of instances from your catalogue.

Full docs of MEF in http://msdn.microsoft.com/en-us/library/dd460648.aspx

1

Either manually implement Factory pattern you need or use any of existing and comfortable for you Dependecy Injection/Inversion of Control containers with common interface, for example IPluging.

For example use this SO answers as base list

How do the major C# DI/IoC frameworks compare?

Community
  • 1
  • 1
Regfor
  • 8,515
  • 1
  • 38
  • 51
0

Basically you are looking for combination of Factory and Adapter patterns.

Wrap up all you objects for which "interface is not an option" into Adapters and use Factory to instantiate them, which in turn will instantiate your weird objects.

Factory design pattern - http://msdn.microsoft.com/en-us/library/ee817667.aspx

Adapter design pattern - http://www.dofactory.com/Patterns/PatternAdapter.aspx

vittore
  • 17,449
  • 6
  • 44
  • 82