Абстрактна фабрика — це породжуючий патерн проектування, який вирішує проблему створення цілих сімейств пов’язаних продуктів, без прив’язки коду до конкретних класів продуктів.
Абстрактна фабрика задає інтерфейс створення всіх доступних типів продуктів, а кожна конкретна реалізація фабрики породжує продукти однієї з варіацій. Клієнтський код викликає методи фабрики для отримання продуктів, замість самостійного створювання їх за допомогою оператора new
. При цьому, фабрика сама стежить за тим, щоб створюваний продукт був потрібної варіації.
Складність:
Популярність:
Застосування: Патерн можна часто зустріти в C#-коді, особливо там, де потрібно породжувати сімейства класів, не прив’язуючи до них свій код (наприклад, у фреймворках).
Ознаки застосування патерна: Патерн можна визначити за методами, що повертають фабрику, яка, в свою чергу, використовується для створення конкретних продуктів, повертаючи їх через абстрактні типи або інтерфейси.
Концептуальний приклад
Цей приклад показує структуру патерна Абстрактна фабрика , а саме — з яких класів він складається, які ролі ці класи виконують і як вони взаємодіють один з одним.
Program.cs: Приклад структури патерна
using System;
namespace RefactoringGuru.DesignPatterns.AbstractFactory.Conceptual
{
// The Abstract Factory interface declares a set of methods that return
// different abstract products. These products are called a family and are
// related by a high-level theme or concept. Products of one family are
// usually able to collaborate among themselves. A family of products may
// have several variants, but the products of one variant are incompatible
// with products of another.
public interface IAbstractFactory
{
IAbstractProductA CreateProductA();
IAbstractProductB CreateProductB();
}
// Concrete Factories produce a family of products that belong to a single
// variant. The factory guarantees that resulting products are compatible.
// Note that signatures of the Concrete Factory's methods return an abstract
// product, while inside the method a concrete product is instantiated.
class ConcreteFactory1 : IAbstractFactory
{
public IAbstractProductA CreateProductA()
{
return new ConcreteProductA1();
}
public IAbstractProductB CreateProductB()
{
return new ConcreteProductB1();
}
}
// Each Concrete Factory has a corresponding product variant.
class ConcreteFactory2 : IAbstractFactory
{
public IAbstractProductA CreateProductA()
{
return new ConcreteProductA2();
}
public IAbstractProductB CreateProductB()
{
return new ConcreteProductB2();
}
}
// Each distinct product of a product family should have a base interface.
// All variants of the product must implement this interface.
public interface IAbstractProductA
{
string UsefulFunctionA();
}
// Concrete Products are created by corresponding Concrete Factories.
class ConcreteProductA1 : IAbstractProductA
{
public string UsefulFunctionA()
{
return "The result of the product A1.";
}
}
class ConcreteProductA2 : IAbstractProductA
{
public string UsefulFunctionA()
{
return "The result of the product A2.";
}
}
// Here's the the base interface of another product. All products can
// interact with each other, but proper interaction is possible only between
// products of the same concrete variant.
public interface IAbstractProductB
{
// Product B is able to do its own thing...
string UsefulFunctionB();
// ...but it also can collaborate with the ProductA.
//
// The Abstract Factory makes sure that all products it creates are of
// the same variant and thus, compatible.
string AnotherUsefulFunctionB(IAbstractProductA collaborator);
}
// Concrete Products are created by corresponding Concrete Factories.
class ConcreteProductB1 : IAbstractProductB
{
public string UsefulFunctionB()
{
return "The result of the product B1.";
}
// The variant, Product B1, is only able to work correctly with the
// variant, Product A1. Nevertheless, it accepts any instance of
// AbstractProductA as an argument.
public string AnotherUsefulFunctionB(IAbstractProductA collaborator)
{
var result = collaborator.UsefulFunctionA();
return $"The result of the B1 collaborating with the ({result})";
}
}
class ConcreteProductB2 : IAbstractProductB
{
public string UsefulFunctionB()
{
return "The result of the product B2.";
}
// The variant, Product B2, is only able to work correctly with the
// variant, Product A2. Nevertheless, it accepts any instance of
// AbstractProductA as an argument.
public string AnotherUsefulFunctionB(IAbstractProductA collaborator)
{
var result = collaborator.UsefulFunctionA();
return $"The result of the B2 collaborating with the ({result})";
}
}
// The client code works with factories and products only through abstract
// types: AbstractFactory and AbstractProduct. This lets you pass any
// factory or product subclass to the client code without breaking it.
class Client
{
public void Main()
{
// The client code can work with any concrete factory class.
Console.WriteLine("Client: Testing client code with the first factory type...");
ClientMethod(new ConcreteFactory1());
Console.WriteLine();
Console.WriteLine("Client: Testing the same client code with the second factory type...");
ClientMethod(new ConcreteFactory2());
}
public void ClientMethod(IAbstractFactory factory)
{
var productA = factory.CreateProductA();
var productB = factory.CreateProductB();
Console.WriteLine(productB.UsefulFunctionB());
Console.WriteLine(productB.AnotherUsefulFunctionB(productA));
}
}
class Program
{
static void Main(string[] args)
{
new Client().Main();
}
}
}
Output.txt: Результат виконання
Client: Testing client code with the first factory type...
The result of the product B1.
The result of the B1 collaborating with the (The result of the product A1.)
Client: Testing the same client code with the second factory type...
The result of the product B2.
The result of the B2 collaborating with the (The result of the product A2.)