Wzorzec projektowy Twoim wzorcem cz.1. Metoda fabryczna / Design Pattern is your pattern pt.1. Factory Method

PL

Wprowadzenie

Dzisiaj przedstawię Wam temat wzorców projektowych, a głównie pierwszy wzorzec projektowy, który wybrałem jakim jest Metoda fabryczna – Factory Method. Powiem, czemu je stosować, jakie istnieją rodzaje wzorców projektowych, jakie istnieją poszczególne wzorce projektowe, a na samym końcu wpisu zamieszczę przykładową implementację danego wzorca w języku C#.

Wzorzec projektowy jest pewnym schematem, ogólnym rozwiązaniem pewnego problemu, na podstawie którego powstaje potem konkretna implementacja. Wzorzec projektowy nie jest gotową implementacją, którą można zaaplikować w projekcie, jest abstrakcyjnym opisem pożądanego zachowania pewnej sytuacji lub jego abstrakcyjną implementacją.

Wzorce projektowe pozwalają na rozwiązywanie problemów architektonicznych, implementacyjnych oraz wydajnościowych, dlatego też w przypadku dużych lub rozproszonych aplikacji ich użycie jest wręcz konieczne w celu uzyskania dobrego performance’u.

Oczywiście wzorce projektowe wpierają dobre praktyki projektowania i programowania aplikacji, a nawet modelowania jej dziedziny na etapie początkowym Domain-Driven Design, dlatego też ich używanie podczas codziennego developmentu jest wskazane.

Zaletą stosowania wzorców projektowych jest też fakt, że ich spójność oraz unifikacja pozwala programistom oraz projektantom na większe zrozumienie wzajemnych implementacji, co wspiera umocnienie pozycji zalecanego w projektach informatycznych tzw. Języka Wszechobecnego, którego ideę szerzy Eric Evans, autor książki Domain-Driven Design: Tackling Complexity in the Heart of Software, o polskim tytule Domain-Driven Design. Zapanuj nad złożonym systemem informatycznym.

Wzorce kreacyjne

Wzorce kreacyjne wspierają, jak wskazuje nazwa, tworzenie. Tym tworzeniem może być tworzenie klas oraz tworzenie obiektów, czyli w nawiązaniu do klas, mowa o powoływaniu instancji klas za pomocą abstrakcji lub z użyciem interfejsów.

Metoda fabryczna (Factory Method)

Celem Metody fabrycznej jest dostarczenie interfejsu, fabryki, której odpowiedzialność będzie polegać na powoływaniu instancji określonej klasy domeny pośrednio z użyciem właśnie wspomnianej fabryki.

Factory method zazwyczaj implementuje się z wykorzystaniem interfejsu, w przypadku wzorca Abstract factory często wykorzystuje się w implementacji klasę abstrakcyjną jako klasę factory.

Ważne jest, że wzorzec Factory Method zazwyczaj idzie w parze z jedną klasą domeny, natomiast w przypadku zastosowania wzorca Abstract Factory często mamy do czynienia z rodziną klas domeny.

W ciele metody klasy factory powołujemy instancję klasy domeny. W wielu przypadkach widziałem, że wywoływany jest wtedy klasycznie konstruktor klasy domeny, co jest poprawnym rozwiązaniem z punktu widzenia architektury. Natomiast w przykładzie implementacji umieszczonym pod postem, zastosowałem użycie statycznej metody kreacyjnej.

Zastosowałem taką implementację ze względu na dobrą praktykę DDD oraz technikę, i tutaj jeszcze raz odwołam się do pozycji Domain-Driven Design. Zapanuj nad złożonym systemem informatycznym Erica Evans’a mówiącą, że klasa domeny nie powinna mieć odpowiedzialności polegającej na powoływania instancji tej właśnie klasy.

Poniżej pytania dotyczącego samego stosowania Metody Fabrycznej, takie pytania i odpowiedzi na nie będę stawiał w każdym wpisie dotyczącym wzorców projektowych.

Czemu?

  • Zapewnia enkapsulację powoływania nowej instancji klasy oraz nie wymaga użycia słowa kluczowego new;
  • Wspomaga panowanie nad instancjami konkretnej klasy, przez fakt istnienia tylko jednej instancji danego factory w życiu aplikacji;
  • Klient nigdy nie będzie musiał powoływać klasy domeny ani jej elementu bezpośrednio, tylko przy użyciu dostarczonego factory;
  • Umożliwia zastosowanie optymalnej konstrukcji statycznej metody kreacyjnej klasy domeny

Jak?

  • Dostarcza interfejs będący fabryką do tworzenia instancji klasy domeny
  • Klasa domeny jest powoływana i modelowana przy pomocy powyżej wspomnianego interfejsu fabryki
  • Interfejs i jego implementacja, czyli właściwe factory, dostarcza metodę powołującą instancję klasy domeny
  • Wspomniana powyżej metoda wywołuje statyczną metodę kreacyjną zaimplementowaną w klasie domeny
  • Dodatkowo factory może zawierać metody modelujące instancję klasy domeny, czyli po prostu modyfikujące jej stan

Przykładowa implementacja w C#


    /// 
<summary>
    /// factory interface that delivers GetProduct responsible for retrieving Product instance from factory
    /// </summary>

    public interface IFactory
    {
        void SetProduct();
        Product GetProduct();
    }
    /// 
<summary>
    /// Factory that delivers such services like setting Product instance to the current property
    /// and retrieving Product instance
    /// </summary>

    public class Factory : IFactory
    {
        public Factory()
        {
        }

        /// 
<summary>
        /// Product property 
        /// </summary>

        public Product Product { get; set; }

        /// 
<summary>
        /// assigns created Product's instance to the current Product property
        /// </summary>

        /// <param name="product">instance of Product's class</param>
        public void SetProduct()
        {
            this.Product = Product.Create();
        }

        /// 
<summary>
        /// delivers  current Product property
        /// </summary>

        /// <returns>Product property</returns>
        public Product GetProduct()
        {
            return this.Product;
        }
    }
    /// 
<summary>
    /// Target Product class
    /// </summary>

    public class Product
    {
        public string Name { get; set; }

        /// 
<summary>
        /// delivers new Product's instance
        /// </summary>

        /// <returns>Product's instance</returns>
        public static Product Create()
        {
            return new Product();
        }

        /// 
<summary>
        /// sets value of property Name of the current instance
        /// </summary>

        /// <param name="name">string name</param>
        public void SetName(string name)
        {
            this.Name = name;
        }
    }

Przykładowe powołanie instancji klasy domeny z wykorzystaniem buildera BuilderA oraz kontenera Dependency Injection Autofac:


DesignPatternsDiConfig.RegisterTypes();

var factory = DesignPatternsDiConfig.factoryMethodfactory;
factory.SetProduct();

var product = factory.GetProduct();

Console.WriteLine(product);

,gdzie klasa DesignPatternsDiConfig ma następującą implementację:


/// 
<summary>
    /// responsible for registrating and resolving types using IoC container
    /// </summary>

    public class DesignPatternsDiConfig
    {
        private static IContainer _container;
       
        public static DesignPatterns.CreationalPatterns.FactoryMethod.Interfaces.IFactory factoryMethodfactory;

        /// 
<summary>
        /// registers and resolve delivered types according to the IoC container
        /// </summary>

        public static void RegisterTypes()
        {
            var builder = new ContainerBuilder();

            // Factory Method factories registering
            builder.RegisterType<DesignPatterns.CreationalPatterns.FactoryMethod.Factories.Factory>().AsSelf();
            builder.RegisterType<DesignPatterns.CreationalPatterns.FactoryMethod.Factories.Factory>()
                .As<DesignPatterns.CreationalPatterns.FactoryMethod.Interfaces.IFactory>();

            //
            // builds container -> important: can be performed only once within current application context's live
            //
            _container = builder.Build();

            //resolving Factory Method factory
            factoryMethodfactory = _container.Resolve<CreationalPatterns.FactoryMethod.Factories.Factory>();
        }
    }

Wróć na stronę startową

ENG

Introduction

Today I am going to present design patterns, and to be more accurate, the first design pattern I’ve chosen, which is Fabric Method. I will tell you why we should use them, what kinds of them and what pariticular of them exist and finally in the end of the post I placed example of its implementation in C#.

Design pattern is some scheme, general solution of problem, on base which concrete implementation is created. Design pattern is not a ready implementation which can be applied in a project, it is an abstract description of desirable behaviour some situation or its abstract implementation.

Design patterns allow to solve architectonic, implementation and efficiency problems, according to this in case of big or distributed applications, using them is necessary for achieving well performance.

Of course design patterns support good practices of projecting and programming applications, and even its domain modelling in the beginning stage of Domain-Driven Design, therefore using them while daily development is desirable.

Advantage of using design patterns is that its cohesion and unification allow programmers and designers to better understand each one’s implementations, which supports growth of the position of advised in IT projects so-called Ubiquitous Language, which idea is being spread by Erica Evans, author of the book Domain-Driven Design: Tackling Complexity in the Heart of Software.

Creational Patterns

Creational patterns support, as the name indicates, creation processes. Mentioned creation can be creating of classes’ instances or creating of objects, so according to classes, we talk about creating of classes with abstractions or interfaces.

Factory Method

The goal of the Factory Method is to deliver an interface, factory, which would be responsible for invoking instance of an instance of concrete domain class, directly using mentioned factory.

Factory Method usually is being implemented using interface construction, on the other hand, when it comes to Abstract Factory design pattern usually abstract class’s construction is being used, and it plays role of factory class.

Important thing is that Factory Method is usually related with one domain class while Abstract Factory often meets with family of domain classes.

Within the body of the method of the factory class instance of the domain class is being invoked. In many cases I have been seen that classic constructor is being invoked to achieve mentioned action, which is of course proper solution. However, in the example that I placed in the end of the post I used static creational method construction.

I applied abovementioned technique because of good practices concerning Domain-Driven Design and technique, and once again I mention Eric Evans’s DDD book which says that a domain class should not be able to have responsibility based on invoking instance of itself.

Below questions concerning usage of the Fabric Method, these questions and answers to them I will always place in every post concerning design patterns.

Why?

  • Provides encapsulation of invoking new class’s instance and does not require using new keyword
  • Supports controlling over instances of a concrete class by fact existing only one instance of a factory in application lifetime
  • Client would never has to invoke domain class’s instance nor its element directly, but with using delivered factory
  • Enables application of optimal construction of a creational static method implemented within domain class

How?

  • Delivers interface being factory with responsibility of creating domain class’s instance
  • Domain class is being invoked and modeled with abovementioned factory interface
  • Interface and its implementation, so exactly factory, delivers method invoking domain class’s instance
  • Additionally, factory can implement methods modelling domain class’s instance, that is modifying its state

Example of implementation in C#


    /// 
<summary>
    /// factory interface that delivers GetProduct responsible for retrieving Product instance from factory
    /// </summary>

    public interface IFactory
    {
        void SetProduct();
        Product GetProduct();
    }
    /// 
<summary>
    /// Factory that delivers such services like setting Product instance to the current property
    /// and retrieving Product instance
    /// </summary>

    public class Factory : IFactory
    {
        public Factory()
        {
        }

        /// 
<summary>
        /// Product property 
        /// </summary>

        public Product Product { get; set; }

        /// 
<summary>
        /// assigns created Product's instance to the current Product property
        /// </summary>

        /// <param name="product">instance of Product's class</param>
        public void SetProduct()
        {
            this.Product = Product.Create();
        }

        /// 
<summary>
        /// delivers  current Product property
        /// </summary>

        /// <returns>Product property</returns>
        public Product GetProduct()
        {
            return this.Product;
        }
    }
    /// 
<summary>
    /// Target Product class
    /// </summary>

    public class Product
    {
        public string Name { get; set; }

        /// 
<summary>
        /// delivers new Product's instance
        /// </summary>

        /// <returns>Product's instance</returns>
        public static Product Create()
        {
            return new Product();
        }

        /// 
<summary>
        /// sets value of property Name of the current instance
        /// </summary>

        /// <param name="name">string name</param>
        public void SetName(string name)
        {
            this.Name = name;
        }
    }

Example of invoking instance of domain class using BuilderA builder and DI Autofac Container:


DesignPatternsDiConfig.RegisterTypes();

var factory = DesignPatternsDiConfig.factoryMethodfactory;
factory.SetProduct();

var product = factory.GetProduct();

Console.WriteLine(product);

where DesignPatternsDiConfig class has the following implementation:


/// 
<summary>
    /// responsible for registrating and resolving types using IoC container
    /// </summary>

    public class DesignPatternsDiConfig
    {
        private static IContainer _container;
       
        public static DesignPatterns.CreationalPatterns.FactoryMethod.Interfaces.IFactory factoryMethodfactory;

        /// 
<summary>
        /// registers and resolve delivered types according to the IoC container
        /// </summary>

        public static void RegisterTypes()
        {
            var builder = new ContainerBuilder();

            // Factory Method factories registering
            builder.RegisterType<DesignPatterns.CreationalPatterns.FactoryMethod.Factories.Factory>().AsSelf();
            builder.RegisterType<DesignPatterns.CreationalPatterns.FactoryMethod.Factories.Factory>()
                .As<DesignPatterns.CreationalPatterns.FactoryMethod.Interfaces.IFactory>();

            //
            // builds container -> important: can be performed only once within current application context's live
            //
            _container = builder.Build();

            //resolving Factory Method factory
            factoryMethodfactory = _container.Resolve<CreationalPatterns.FactoryMethod.Factories.Factory>();
        }
    }

Get back to Home Page

1 komentarz do wpisu “Wzorzec projektowy Twoim wzorcem cz.1. Metoda fabryczna / Design Pattern is your pattern pt.1. Factory Method

  1. Pingback: dotnetomaniak.pl

Dodaj komentarz