none
C# Factory Pattern RRS feed

  • Question

  • 
    
    
    public abstract class Message
        {
            public abstract string MessageId { get; set; }
            public abstract string MessageString { get; set; }
        }
    ================================================================================================
    public abstract class MessageFactory
        {
            protected string messagesFilePath;
            public abstract string GetMessage(string lcid, string id);
            public abstract List<Message> GetMessageList(string lcid);
            public abstract Dictionary<string, string> GetMessageDictionary(string lcid);
            public abstract void SetDefaultLanguage();
            public MessageFactory(string[] args)
            {
                this.messagesFilePath = args[0];
            }
        }
    ================================================================================================
    public class DefsLCID
        {
            public const int LCID_ENGLISH_UNITED_STATES_0409_1033 = 1033;
            public const int LCID_JAPANESE_0411_1041 = 1041;
        }
    ================================================================================================
    public class DefsXml
        {
            public const string XML_MESSAGE_CONFIGURATION = "MessageConfiguration";
            public const string XML_DEFAULTLANGUAGELCID = "DefaultLanguageLCID";
            public const string XML_CULTURES = "Cultures";
            public const string XML_CULTURE = "Culture";
            public const string XML_LCID = "lcid";
            public const string XML_ADD = "add";
            public const string XML_KEY = "key";
            public const string XML_VALUE = "value";
        }
    ================================================================================================
    namespace MessageFactoryCore
    {
        public class MessageFactoryXML : MessageFactory
        {
            public XmlDocument MessagesXml { get; set; }
            public string MessagesFilePath { get; set; }
            int DefaultCultureLCID { get; set; }
    
            public MessageFactoryXML(string[] args)
                : base(args)
            {
                MessagesFilePath = args[0];
                try
                {
                    MessagesXml = LoadMessagesXml();
                    SetDefaultLanguage();
                }
                catch (Exception)
                {
                    throw;
                }
            } 
    
            private XmlDocument LoadMessagesXml()
            {
                XmlDocument result = new XmlDocument();
                try
                {
                    result.Load(MessagesFilePath);
                }
                catch (Exception)
                {
                    throw;
                }
                return result;
            }
    
            public override string GetMessage(string lcid, string id)
            {
                string result = string.Empty;
                try
                {
                    XDocument doc = XDocument.Parse(MessagesXml.InnerXml.ToString());
                    var query = doc.Element(DefsXml.XML_MESSAGE_CONFIGURATION)
                        .Element(DefsXml.XML_CULTURES)
                        .Elements(DefsXml.XML_CULTURE)
                        .Where(x => x.Attribute(DefsXml.XML_LCID).Value == lcid)
                        .First().Elements(DefsXml.XML_ADD)
                        .Where(k => k.Attribute(DefsXml.XML_KEY).Value == id);
                    result = query.First().Attribute(DefsXml.XML_VALUE).Value;
                }
                catch (Exception)
                {
                    throw;
                }
                return result;
            }
    
            public override List<Message> GetMessageList(string lcid)
            {
                List<Message> msgList = null;
                try
                {
                    XDocument doc = XDocument.Parse(MessagesXml.InnerXml.ToString());
                    msgList = doc.Element(DefsXml.XML_MESSAGE_CONFIGURATION)
                        .Element(DefsXml.XML_CULTURES)
                        .Elements(DefsXml.XML_CULTURE)
                        .Where(x => x.Attribute(DefsXml.XML_LCID).Value == lcid).Descendants()
                        .Select(x => new MessageXml()
                        {
                            MessageId = x.Attribute(DefsXml.XML_KEY).Value,
                            MessageString = x.Attribute(DefsXml.XML_VALUE).Value
                        })
                        .ToList<Message>();
    
                }
                catch (Exception)
                {
                    throw;
                }
                return msgList;
            }
    
            public override Dictionary<string, string> GetMessageDictionary(string lcid)
            {
                Dictionary<string, string> msgDictionary = null;
                try
                {
                    XDocument doc = XDocument.Parse(MessagesXml.InnerXml.ToString());
                    msgDictionary = doc.Element(DefsXml.XML_MESSAGE_CONFIGURATION)
                         .Element(DefsXml.XML_CULTURES)
                         .Elements(DefsXml.XML_CULTURE)
                         .Where(x => x.Attribute(DefsXml.XML_LCID).Value == lcid).Descendants()
                         .ToDictionary(p => p.Attribute(DefsXml.XML_KEY).Value, p => p.Attribute(DefsXml.XML_VALUE).Value);
    
                }
                catch (Exception)
                {
                    throw;
                }
                return msgDictionary;
            }
    
            public override void SetDefaultLanguage()
            {
                try
                {
                    XDocument doc = XDocument.Parse(MessagesXml.InnerXml.ToString());
                    var query = doc.Element(DefsXml.XML_MESSAGE_CONFIGURATION)
                        .Element(DefsXml.XML_DEFAULTLANGUAGELCID)
                        .Attribute(DefsXml.XML_LCID).Value;
                    DefaultCultureLCID = int.Parse(query);
                }
                catch (Exception)
                {
                    DefaultCultureLCID = -1;
                    throw;
                }
            }
        }
    }
    ================================================================================================
    namespace MessageFactoryCore
    {
        public class MessageXml : Message
        {
            public override string MessageId { get; set; }
            public override string MessageString { get; set;}
        }
    }
    ================================================================================================
    
    namespace MessageFactoryCore
    {
        public static class MessageMessenger
        {
            public static string ServeMessage(MessageFactory factory, string lcid, string id)
            {
                return factory.GetMessage(lcid, id);
            }
        }
    }
    
    <?xml version="1.0" encoding="utf-8" ?>
    <MessageConfiguration>
      <DefaultLanguageLCID lcid="1041" />
    <Cultures>
      <Culture lcid="1041">
        <add key="0001" value="asdfasdf" />
        <add key="0002" value="asdfasdf" />
      </Culture>
      <Culture lcid="1033">
        <add key="0001" value="This is a test" />
        <add key="0002" value="This is another test" />
      </Culture>
    </Cultures>
    </MessageConfiguration>
    
     private void button1_Click(object sender, EventArgs e)
            {
                string[] args = { @"C:\Users\wooley\Documents\Visual Studio 2010\Projects\Messaging\MessageFactory\Messages.xml" };
                MessageFactory factory = new NewFactory(args);
                MessageBox.Show(MessageMessenger.ServeMessage(
                    factory, 
                    DefsLCID.LCID_ENGLISH_UNITED_STATES_0409_1033.ToString(), 
                    "0001"));
            }


    Friday, June 1, 2012 9:11 AM

Answers

  • Well for that the simplest form of DI is constructor injection. So you pass the required dependency to your instance via constructor. You should read the following Martin Fowler's article.

    For example in the following, the service don't actually care the data access implementation it just knows that via repository passed to it users are received from some data source. Now you can pass either one of the actual UserRepository implementations (MySql or SqlServer) to the UserService usually based on some configuration either with DI container, the preferred way, or using so called Poor-Man's DI by composing the dependency by yourself for example in application startup.

    When we use factory pattern with DI, that usually is Abstract Factory pattern (link 1 & link2), where the abstract factory provides us the correct factory instance to create some other dependency that we only know at the runtime. For example if we need to change the runtime algorithm of some action based on user selections, like calculating something base on user location, time or other used preference.

    class UserService
    {
        private readonly UserRepository repository;
     
        // take dependency in ctor
        public UserService(UserRepository repository)
        {
              if (repository == null)
                throw ArgumentNullException("repository");
             
             this.repository = repository;
        }
    
        public IEnumerable<User> GetUsers()
        {
              // use dependency
              this.repository.GetAllUsers();
        }
    }
    
    abstract class UserRepository
    {
        public abstract IEnumerable<User> GetAllUsers();
    }
    
    class MySqlUserRepository : UserRepository
    {
       public override IEnumerable<User> GetAllUsers()
       {
           // do MySql data access query
       }
    }
    
    class SqlServerUserRepository : UserRepository
    {
        public override IEnumerable<User> GetAllUsers()
        {
            // do SQL server data access
        }
    }


    • Edited by MasaSam Friday, June 1, 2012 3:59 PM
    • Marked as answer by Alan D. Wooley Friday, June 1, 2012 9:52 PM
    Friday, June 1, 2012 3:58 PM

All replies

  • Hi, 

    So, what is the question?


    If this post answers your question, please click "Mark As Answer". If this post is helpful please click "Mark as Helpful".

    Friday, June 1, 2012 10:01 AM
  • And the problem is?

    Actually what spotted on my eye immediately is that factory is created in button handler when better alternative would be inject the desired MessageFactory to the view that contains the button handler.

    For DI junkies all the new is evil ; )

    Friday, June 1, 2012 11:18 AM
  • Hey, what happened to my question??? MasaSam, that is exactly it.  With the Factory Pattern, how would I inject this?  It's a good start, perhaps, but the dependency is already there.  What would be a good way to IoC this? And please be gentle - this is my first round of trying to apply an actual pattern. I am a complete newbie, but I can already see the benefits of patterns.  What is all this DI about and is there something generically simple so I don't have to study both patterns AND DI at the same time?
    Friday, June 1, 2012 12:52 PM
  • Well for that the simplest form of DI is constructor injection. So you pass the required dependency to your instance via constructor. You should read the following Martin Fowler's article.

    For example in the following, the service don't actually care the data access implementation it just knows that via repository passed to it users are received from some data source. Now you can pass either one of the actual UserRepository implementations (MySql or SqlServer) to the UserService usually based on some configuration either with DI container, the preferred way, or using so called Poor-Man's DI by composing the dependency by yourself for example in application startup.

    When we use factory pattern with DI, that usually is Abstract Factory pattern (link 1 & link2), where the abstract factory provides us the correct factory instance to create some other dependency that we only know at the runtime. For example if we need to change the runtime algorithm of some action based on user selections, like calculating something base on user location, time or other used preference.

    class UserService
    {
        private readonly UserRepository repository;
     
        // take dependency in ctor
        public UserService(UserRepository repository)
        {
              if (repository == null)
                throw ArgumentNullException("repository");
             
             this.repository = repository;
        }
    
        public IEnumerable<User> GetUsers()
        {
              // use dependency
              this.repository.GetAllUsers();
        }
    }
    
    abstract class UserRepository
    {
        public abstract IEnumerable<User> GetAllUsers();
    }
    
    class MySqlUserRepository : UserRepository
    {
       public override IEnumerable<User> GetAllUsers()
       {
           // do MySql data access query
       }
    }
    
    class SqlServerUserRepository : UserRepository
    {
        public override IEnumerable<User> GetAllUsers()
        {
            // do SQL server data access
        }
    }


    • Edited by MasaSam Friday, June 1, 2012 3:59 PM
    • Marked as answer by Alan D. Wooley Friday, June 1, 2012 9:52 PM
    Friday, June 1, 2012 3:58 PM
  • Thanks kindly.  I'm going to give this a shot. Apprecciate your guidance.

    Alan

    Friday, June 1, 2012 9:53 PM
  • // Interfaces
        public interface IMessage
        {
            int DefaultLCID { get; set; }
            string GetMessage(int lcid, string messageID);
        }
       public interface IMessageFactory
        {
            IMessage CreateMessageGrip(string[] args);
        }
    // Factory Machinery
        public class DefsLCID
        {
            public const int LCID_JAPANESE_0411_1041 = 1041;
            public const int LCID_ENGLISH_UNITED_STATES1033 = 1033;
        }
        public class DefsMessageIDs
        {
            public const string C_MESSAGE_0001 = "10001";
            public const string C_MESSAGE_0002 = "10002";
        }
        public class MessageFactoryCreator
        {
            public static IMessageFactory CreateMessageFactory(string messageFactoryType)
            {
                IMessageFactory factory = null;
                switch (messageFactoryType)
                {
                    case "XML":
                        factory = new MessageFactoryXML();
                        break;
                    default:
                        break;
                }
                return factory;
            }
        }
    // Products
        public class MessageXML : IMessage
        {
            public int DefaultLCID { get; set; }
            public XElement Messages { get; set; }
    
            public MessageXML(string path)
            {
                if (!string.IsNullOrEmpty(path))
                {
                    if (!File.Exists(path))
                    {
                        return;
                    }
                    using (StreamReader sr = new StreamReader(path, Encoding.UTF8))
                    {
                        var doc = XDocument.Parse(sr.ReadToEnd());
                        // Load default LCID
                        var lcid = doc.Element(DefsMessageXML.XML_MESSAGE_CONFIGURATION)
                       .Element(DefsMessageXML.XML_DEFAULTLANGUAGELCID)
                       .Attribute(DefsMessageXML.XML_LCID).Value;
                        DefaultLCID = int.Parse(lcid);
    
                        // Load Messages Node into XElement
                        Messages = doc.Element(DefsMessageXML.XML_MESSAGE_CONFIGURATION)
                            .Element(DefsMessageXML.XML_CULTURES);
                    }
                }
            }
    
            public string GetMessage(int lcid, string messageID)
            {
                string result = string.Empty;
                try
                {
                    var lcidExists = Messages.Elements(DefsMessageXML.XML_CULTURE)
                        .Any(x => x.Attribute(DefsMessageXML.XML_LCID).Value == lcid.ToString());
    
                    if (!lcidExists) lcid = DefaultLCID;
    
                    // Retrieve message based on lcid and message ID
                    var message = Messages.Elements(DefsMessageXML.XML_CULTURE)
                        .Where(x => x.Attribute(DefsMessageXML.XML_LCID).Value == lcid.ToString())
                            .First().Elements(DefsMessageXML.XML_ADD)
                                .Where(k => k.Attribute(DefsMessageXML.XML_KEY).Value == messageID);
                    result = message.First().Attribute(DefsMessageXML.XML_VALUE).Value;
                }
                catch (Exception)
                {
                }
    
                return result;
            }
        }
    // Sample Message XML
    <?xml version="1.0" encoding="utf-8" ?>
    <MessageConfiguration>
      <DefaultLanguageLCID lcid="1041" />
      <Cultures>
        <Culture lcid="1041">
          <add key="10001" value="テストメッサージ①" />
          <add key="10002" value="テストメッサージ②" />
        </Culture>
        <Culture lcid="1033">
          <add key="10001" value="Test Message #1" />
          <add key="10002" value="Test Message #2" />
        </Culture>
      </Cultures>
    </MessageConfiguration>
    
    // Factory Client
            static void Main(string[] args)
            {
               var messagePool = MessageFactoryCreator.CreateMessageFactory("XML").CreateMessageGrip(args);
    
                Console.Write(messagePool.GetMessage(
                    DefsLCID.LCID_JAPANESE_0411_1041,
                    DefsMessageIDs.C_MESSAGE_0001));
    
                Console.Write(Environment.NewLine);
    
                Console.Write(messagePool.GetMessage(
                    DefsLCID.LCID_ENGLISH_UNITED_STATES_1033,
                    DefsMessageIDs.C_MESSAGE_0002));
                
                Console.Write(Environment.NewLine);
    
                Console.ReadLine();
            }
    

    This is an example of a more finished product for my first stab at the Abstract Factory.

    MasaSam, if I can get your impressions on this, I would be grateful.  Also, upon your suggestion, I am thinking of using an IoC container. I have my eye on Ninject and Simple Injectory. Do you have any advice on this?

    Best Regards, Alan

    Friday, June 8, 2012 6:05 AM
  • You might need couple of more assemblies to do the abstraction if you want to.

    What comes to DI containers there is no real difference in those I've used, Ninject, Castle Windsor, StructureMap, Unity, Autofac, so I recommend to try some and select that seems best for you. Lately I've been using Autofac, but that's just because of their WCF / MVC integration.

    You should also read the book Dependency Injection in .NET by Mark Seeman, if you really are interested about DI and going to used in future.

    Example of mine without any actual implementation would be

        /* interfaces implemented in common.dll */
    
        public interface IMessage
        {
            string GetMessage(string formatting);
        }
    
        public interface IMessageFactory
        {
            IMessage CreateMessage();
        }
    
        public interface IMessageFactoryProvider
        {
            IMessageFactory CreateProvider(MessageType type);
        }
    
        public enum MessageType
        {
            Xml,
            Plain
        }
    
        /* abstract implementations in core.dll 
            
         *  refecences: 
         *              common.dll
         */
        public abstract class Message : IMessage
        {
            public abstract string GetMessage(string formatting);
        }
    
        public abstract class MessageFactoryProvider : IMessageFactoryProvider
        {
            public abstract IMessageFactory CreateProvider(MessageType type);
        }
    
        /* Xml message implementations in core_xml.dll 
            
         * references:
         *              common.dll
         *              core.dll
         */
        public class XmlMessageFactory : IMessageFactory
        {
            public IMessage CreateMessage()
            {
                return new XmlMessage();
            }
        }
    
        public class XmlMessage : Message
        {
            public override string GetMessage(string formatting)
            {
                throw new NotImplementedException();
            }
        }
    
        /* Plain message implementations in core_plain.dll
         * 
         * references:
         *              common.dll
         *              core.dll
         */
        public class PlainMessage : Message
        {
            public override string GetMessage(string formatting)
            {
                throw new NotImplementedException();
            }
        }
    
        public class PlainMessageFactory : IMessageFactory
        {
            public IMessage CreateMessage()
            {
                return new PlainMessage();
            }
        }
    
        /* Provider implementation in core_provider.dll
         * 
         * references:
         *              common.dll
         *              core.dll
         *              core_xml.dll
         *              core_plain.dll
       */
        public class MyMessageFactoryProvider : MessageFactoryProvider
        {
            public override IMessageFactory CreateProvider(MessageType type)
            {
                if (type == MessageType.Xml)
                {
                    return new XmlMessageFactory();
                }
    
                return new PlainMessageFactory();
            }
        }
    
        /* Consuming class implemented in app.dll references common.dll */
        /*
         *  references:
         *               common.dll
         */
        /* provider will be injected by the host */
        /* this might be for example MVP presenter */
        public class MessagePresenter
        {
            private IMessageFactoryProvider provider;
    
            public MessagePresenter(IMessageFactoryProvider provider)
            {
                if (provider == null)
                    throw new ArgumentNullException("provider");
    
                this.provider = provider;
            }
        }
    
        /* host application for example windows application in win.exe 
         * 
         * references:
         *              app.dll
         */
        public class Host
        {
            private DIContainer container;
    
            public void Main()
            {
                container = new DIContainer();
                container.Configure();
    
                IMessageFactoryProvider provider = container.GetInstance<IMessageFactoryProvider>();
                MessagePresenter presenter = new MessagePresenter(provider);
            }
        }
    
        /* class from DI containers assembly */
        public class DIContainer
        {
            public T GetInstance<T>()
            {
                // this would resolve the dependency
                return default(T);
            }
    
            public void Configure()
            {
                // this would do the mapping from
                // concrete class to interface
                // for example
                
                // this.CreateInstance<MyMessageFactoryProvider>().As<IMessageFactoryProvider>();
            }
        }

    Friday, June 8, 2012 8:15 AM
  • MasaSam, Thanks for walking me through that. Looks like I've still got a lot to learn. And thanks for your extra comments on DI containers. Best Regards, Alan
    Friday, June 8, 2012 8:50 AM