Abstract Factory Pattern in C#

Giới thiệu

Hẳn đã là 1 developer có tâm thì bạn luôn muốn code của mình viết ra không chỉ dễ đọc, dễ hiểu mà còn có thể dễ bảo trì. Design parterns chắc chắn là cảnh giới mà mỗi developer chất luôn muốn đạt được để có thể nâng tầm code mà mình viết ra. Đó còn là giải pháp cho các vấn đề về thiết kế mà bạn sẽ thấy rất nhiều trong việc phát triển phần mềm trong thực tế. Patterns ở đây chính là việc các object có thể được sử dụng lại một cách khôn ngoan thông qua cấu trúc và cách nó tương tác với các object khác. Quyển "The 23 Gang of Four (GoF) Patterns" chứa đựng những pattern thường được dùng làm nền tảng cho những pattern khác, chia ra làm 3 nhóm: Creational, Structural và Behavioral.

Trong bài viết hôm nay mình sẽ giới thiệu cho các bạn về Abstract Factory Pattern, một pattern thường được sử dụng để tạo ra những object có những điểm chung và có những tính chất riêng.

Định nghĩa

Abstract Factory pattern giống như 1 siêu nhà máy sản suất, để tạo ra những nhà máy khác (để tạo ra object). Siêu máy này còn được gọi là máy của các máy (factory of factories). Kiểu design pattern này là 1 kiểu của creational pattern, và là 1 trong những cách hay và hiệu quả nhất để tạo ra các object. Trong Abstract Factory pattern, 1 interface chịu trách nhiệm để tạo ra 1 nhà máy cho những object tương tự nhau mà không chỉ định rõ ra class của chúng. Mỗi nhà máy tạo ra có thể đưa ra những object theo kiểu nhà máy khác nhau (Factory Pattern). Độ sử dụng thường xuyên: Rất cao

UML Diagram

Các thành phần

  • AbstractFactory (ContinentFactory - nhà máy Lục địa) khai báo 1 interace cho các hàm để tạo ra các abstract product
  • ConcreteFactory (AfricaFactory, AmericaFactory - nhà máy châu Phi, nhà máy châu Mỹ) triển khai hàm từ interface để tạo ra các object product cụ thể
  • AbstractProduct (Herbivore, Carnivore - động vật ăn cỏ, động vật ăn thịt) khai báo 1 interface cho loại product
  • Product (Wildebeest, Lion, Bison, Wolf - dê rừng, sư tử, bò rừng, chó sói)
    • đưa ra những object product được tạo bởi các ConcreteFactory
    • triển khai Abstract Product interface
  • Client (AnimalWorld) sử dụng các interface đã khai báo bới các class AbstarctFactory và AbstractProduct

Cấu trúc code trong C#

using System;
 
namespace DoFactory.GangOfFour.Abstract.Structural
{
  /// <summary>

  /// MainApp startup class for Structural

  /// Abstract Factory Design Pattern.

  /// </summary>

  class MainApp

  {
    /// <summary>

    /// Entry point into console application.

    /// </summary>

    public static void Main()
    {
      // Abstract factory #1

      AbstractFactory factory1 = new ConcreteFactory1();
      Client client1 = new Client(factory1);
      client1.Run();
 
      // Abstract factory #2

      AbstractFactory factory2 = new ConcreteFactory2();
      Client client2 = new Client(factory2);
      client2.Run();
 
      // Wait for user input

      Console.ReadKey();
    }
  }
 
  /// <summary>

  /// The 'AbstractFactory' abstract class

  /// </summary>

  abstract class AbstractFactory

  {
    public abstract AbstractProductA CreateProductA();
    public abstract AbstractProductB CreateProductB();
  }
 
 
  /// <summary>

  /// The 'ConcreteFactory1' class

  /// </summary>

  class ConcreteFactory1 : AbstractFactory

  {
    public override AbstractProductA CreateProductA()
    {
      return new ProductA1();
    }
    public override AbstractProductB CreateProductB()
    {
      return new ProductB1();
    }
  }
 
  /// <summary>

  /// The 'ConcreteFactory2' class

  /// </summary>

  class ConcreteFactory2 : AbstractFactory

  {
    public override AbstractProductA CreateProductA()
    {
      return new ProductA2();
    }
    public override AbstractProductB CreateProductB()
    {
      return new ProductB2();
    }
  }
 
  /// <summary>

  /// The 'AbstractProductA' abstract class

  /// </summary>

  abstract class AbstractProductA

  {
  }
 
  /// <summary>

  /// The 'AbstractProductB' abstract class

  /// </summary>

  abstract class AbstractProductB

  {
    public abstract void Interact(AbstractProductA a);
  }
 
 
  /// <summary>

  /// The 'ProductA1' class

  /// </summary>

  class ProductA1 : AbstractProductA

  {
  }
 
  /// <summary>

  /// The 'ProductB1' class

  /// </summary>

  class ProductB1 : AbstractProductB

  {
    public override void Interact(AbstractProductA a)
    {
      Console.WriteLine(this.GetType().Name +
        " interacts with " + a.GetType().Name);
    }
  }
 
  /// <summary>

  /// The 'ProductA2' class

  /// </summary>

  class ProductA2 : AbstractProductA

  {
  }
 
  /// <summary>

  /// The 'ProductB2' class

  /// </summary>

  class ProductB2 : AbstractProductB

  {
    public override void Interact(AbstractProductA a)
    {
      Console.WriteLine(this.GetType().Name +
        " interacts with " + a.GetType().Name);
    }
  }
 
  /// <summary>

  /// The 'Client' class. Interaction environment for the products.

  /// </summary>

  class Client

  {
    private AbstractProductA _abstractProductA;
    private AbstractProductB _abstractProductB;
 
    // Constructor

    public Client(AbstractFactory factory)
    {
      _abstractProductB = factory.CreateProductB();
      _abstractProductA = factory.CreateProductA();
    }
 
    public void Run()
    {
      _abstractProductB.Interact(_abstractProductA);
    }
  }
}

Kết quả:

ProductB1 interacts with ProductA1
ProductB2 interacts with ProductA2

Cấu trúc trong dự án thực tế

using System;
 
namespace DoFactory.GangOfFour.Abstract.RealWorld
{
  /// <summary>

  /// MainApp startup class for Real-World

  /// Abstract Factory Design Pattern.

  /// </summary>

  class MainApp

  {
    /// <summary>

    /// Entry point into console application.

    /// </summary>

    public static void Main()
    {
      // Create and run the African animal world

      ContinentFactory africa = new AfricaFactory();
      AnimalWorld world = new AnimalWorld(africa);
      world.RunFoodChain();
 
      // Create and run the American animal world

      ContinentFactory america = new AmericaFactory();
      world = new AnimalWorld(america);
      world.RunFoodChain();
 
      // Wait for user input

      Console.ReadKey();
    }
  }
 
 
  /// <summary>

  /// The 'AbstractFactory' abstract class

  /// </summary>

  abstract class ContinentFactory

  {
    public abstract Herbivore CreateHerbivore();
    public abstract Carnivore CreateCarnivore();
  }
 
  /// <summary>

  /// The 'ConcreteFactory1' class

  /// </summary>

  class AfricaFactory : ContinentFactory

  {
    public override Herbivore CreateHerbivore()
    {
      return new Wildebeest();
    }
    public override Carnivore CreateCarnivore()
    {
      return new Lion();
    }
  }
 
  /// <summary>

  /// The 'ConcreteFactory2' class

  /// </summary>

  class AmericaFactory : ContinentFactory

  {
    public override Herbivore CreateHerbivore()
    {
      return new Bison();
    }
    public override Carnivore CreateCarnivore()
    {
      return new Wolf();
    }
  }
 
  /// <summary>

  /// The 'AbstractProductA' abstract class

  /// </summary>

  abstract class Herbivore

  {
  }
 
  /// <summary>

  /// The 'AbstractProductB' abstract class

  /// </summary>

  abstract class Carnivore

  {
    public abstract void Eat(Herbivore h);
  }
 
  /// <summary>

  /// The 'ProductA1' class

  /// </summary>

  class Wildebeest : Herbivore

  {
  }
 
  /// <summary>

  /// The 'ProductB1' class

  /// </summary>

  class Lion : Carnivore

  {
    public override void Eat(Herbivore h)
    {
      // Eat Wildebeest

      Console.WriteLine(this.GetType().Name +
        " eats " + h.GetType().Name);
    }
  }
 
  /// <summary>

  /// The 'ProductA2' class

  /// </summary>

  class Bison : Herbivore

  {
  }
 
  /// <summary>

  /// The 'ProductB2' class

  /// </summary>

  class Wolf : Carnivore

  {
    public override void Eat(Herbivore h)
    {
      // Eat Bison

      Console.WriteLine(this.GetType().Name +
        " eats " + h.GetType().Name);
    }
  }
 
  /// <summary>

  /// The 'Client' class 

  /// </summary>

  class AnimalWorld

  {
    private Herbivore _herbivore;
    private Carnivore _carnivore;
 
    // Constructor

    public AnimalWorld(ContinentFactory factory)
    {
      _carnivore = factory.CreateCarnivore();
      _herbivore = factory.CreateHerbivore();
    }
 
    public void RunFoodChain()
    {
      _carnivore.Eat(_herbivore);
    }
  }
}

Kết quả:

Lion eats Wildebeest
Wolf eats Bison

Kết luận

Với Abstract Pattern thì việc tạo ra các object với những đặc tính có liên quan và đặc tính riêng phát huy được tính trừu tượng trong hướng đối tượng, đồng thời code nhìn sạch sẽ và khoa học. Hy vọng bài viết có ích cho các bạn.

Bài viết tham khảo nguồn từ: http://www.dofactory.com/net/abstract-factory-design-pattern

All Rights Reserved