extension pattern in .NET - Decorate the class with attributes
Consider a simple class hierarchy with an abstract base class and 3 derived classes. A typical implementation of a factory would be a static method on the base class, like this
Problem: The above pattern is bad in different ways
1. Base classes must have references to the sub classes and that makes it tightly coupled.
2. Following the Open-Closed principle (open for extension and closed for modification), we do not want to open the code for changes when in future a new class is introduced that derive from the base class.
I am using the https://en.wikipedia.org/wiki/Factory_pattern
The factory method pattern is an object-oriented creational design pattern to implement the concept of factories and deals with the problem of creating objects (products) without specifying the exact class of object that will be created. The essence of this pattern is to "Define an interface for creating an object, but let the classes that implement the interface decide which class to instantiate. The Factory method lets a class defer instantiation to subclasses."[1]
Solutions: to make our pattern loosly coupled and Open-Closed, I prototyped a quick example in .Net using reflection.
we want some like this.
Prototype: I created the following classes
1. ExtensionClassFactory.cs ----> Implements the Factory
2. ConnectionAttribute.cs ------> Processes the Attributes that decorate the classes.
3. Interface with 2 derived classes (IConnection.cs is the interface, Connection1.cs, Connection2.cs are the derived classes)
Here if you see we are just passing an input to the IConnection interface. Based on the input the corresponding derived class is instantiated.
RESULT ---- "Hi! I am in Connection1" is read from Connection1.cs
How is it done: Implement the following classes.
1. INTERFACE CLASS - that defined the method implemented by derived classes
2. Sample Connection1.cs class that implements derived class1
3. Sample Connection2.cs classs that implements the derived class 2.
4. EXTENSIONFACOTRY - is implemented using REFLECTION to read the attribute that decorates the CLASSES (DERIVED CLASSES
The factory will be given the base class and the attribute value, it will looking for the derived classes and or this attribute type value combination.
Comments
Anonymous
October 21, 2013
method names in C# should start with an uppercase letter. Java and Java Script use camel casing. C# uses pascal casing.Anonymous
October 21, 2013
Very nice... and interesting reading. Thanks.Anonymous
October 22, 2013
- There is no factory method implementation here. Static factory only.
- The approach provided assumes usage of reflection, which is framework-specific, so it can`t be considered as pure solution.
- I`d advise usage of concrete interface per concrete type design and appply the Service Locator pattern. Note: Please read the description again to get the context and essence of this article. - If you do not like static then try without making methods static.
- You can apply the same pattern to pure solutions that you want to develop. use of reflections is only to demonstrate an example.
- Again this is just an example not solving a specific problem. The kind of pattern one uses will vary from problem to problem. Good luck!
- Anonymous
October 23, 2013
Somebody did the string injection in my previous post :) Sir, I got the points. We need to decouple base class from derived and don`t touch the "factory" stuff when derive new class from base. Example above require attributes and reflection to made mapping to be able to resolve the type... actually, why not?. There are no likes or dislikes, just code review and some thoughts :) So...My point is about factory method implementation and more cross-platform solution without reflection magic. Did some exercising on that: public interface IConnection { string Connect(); } public abstract class BaseConnection : IConnection, ICloneable { public abstract string Connect(); public abstract object Clone(); } public class Connection1 : BaseConnection { public override string Connect() { return "Connection1"; } public override object Clone() { return new Connection1(); } } public class Connection2 : BaseConnection { public override string Connect() { return "Connection2"; } public override object Clone() { return new Connection2(); } } public static class TypeResolver { private static readonly IDictionary<string, ICloneable> _typeMapping; static TypeResolver() { _typeMapping = new Dictionary<string, ICloneable> { {"Connect1", new Connection1()}, {"Connect2", new Connection2()} }; } public static object ToObject(this string typeId) { return _typeMapping.ContainsKey(typeId) ? _typeMapping[typeId].Clone() : null; } } class Program { static void Main(string[] args) { TestConnectionObject("Connect1"); TestConnectionObject("Connect2"); Console.ReadLine(); } static void TestConnectionObject(string typeId) { var connection = typeId.ToObject() as IConnection; if (connection != null) Console.WriteLine(connection.Connect()); } }