Содержание
Что такое паттерны проектирования
Паттерн проектирования MVC
Паттерн проектирования MVVM
Паттерн проектирования Фабричный метод
Паттерн проектирования Абстрактная фабрика
Паттерн проектирования Фасад
Паттерн проектирования Барьер

  • Что такое паттерны проектирования

  • Паттерн проектирования MVC

  • Паттерн проектирования MVVM

  • Паттерн проектирования Фабричный метод
Предположим, нам необходимо написать класс для работы с массивами произвольной размерности:
class MyArray
{
    public MyArray(int length) {
        // создание одномерного массива
    }
    public MyArray(int d1, int d2) {
        // создание 2мерного массива
    }
    public MyArray(int[] dims) {
        // создание массива произвольной размерности
    }
}
Создание объектов класса MyArray выглядит тривиальным:
var a = new MyArray(10);
var b = new MyArray(2,5);
var c = new MyArray(new int[]{ 2, 3, 4 });
Но угадать, что, например, делает последний конструктор по его вызову достаточно сложно. Это могло бы быть как создание массива размерностью 2x3x4, так и создание одномерного массива с элементами 2, 3 и 4. Узнать предназначение конструкторов можно по комментариям и всплывающим подсказкам студии.
Для большей наглядности можно использовать именованные конструкторы, по названию которых было бы видно их предназначение. Специальной синтаксической конструкции для именованных конструкторов нет, но нам никто не запрещает написать методы с требуемым функционалом:
class MyArray
{
    public MyArray() {}
    private MyArray(int length) {
    }
    private MyArray(int d1, int d2) {
    }
    private MyArray(int[] dims) {
    }

    #region фабричные методы
    public static MyArray createByDim(int length) {
        return new MyArray(length);
    }
    public static MyArray createByDim(int d1, int d2) {
        return new MyArray(d1,d2);
    }
    public static MyArray createByDim(int[] dims) {
        return new MyArray(dims);
    }
    #endregion
}
Создавать объекты класса MyArray мы теперь должны так:
var a = MyArray.createByDim(10);
var b = MyArray.createByDim(2,5);
var c = MyArray.createByDim(new int[] { 2, 3, 4 });
Обратите внимание, что конструкторы мы спрятали объявлением private, чтобы обязать пользователя создавать экземпляры MyArray только с помощью статичных методов createByDim.
Такие методы (методы, создающие экземпляры класса) и называются фабричными. А пример выше можно назвать реализацией паттерна "фабричный метод"

Кроме того, что фабричные методы предоставляют более наглядный (пусть и менее лаконичный) способ создания экземпляров класса, у таких методов есть и другое важное преимущество - расширяемость. Что могут создать конструкторы класса MyArray? Только объекты типа MyArray. А фабричные методы могут вернуть как экземпляры класса MyArray, так и любого его наследника.
Предположим, что мы создали класс, специально и более эффективно отвечающий за работу с 2мерными массивами:
class MyMatrix : MyArray 
{
    public MyMatrix(int d1, int d2) { }
}
Но хотим предоставить пользователю прозрачный интерфейс работы с матрицами через общий класс MyArray. Для этого изменим фабричный метод класса MyArray:
class MyArray
{   
    // ...
    public static MyArray createByDim(int d1, int d2) {
        return new MyMatrix(d1,d2);
    }
    // ...
}
Процедура же создания массива с двумя размерностями осталась неизменной:
var b = MyArray.createByDim(2,5); // b теперь объект класса MyMatrix!
Используя только конструкторы такой трюк проделать не получилось бы. А с помощью фабричных методов мы отдаем пользователю производный от MyArray класс ни на байт не меняя интерфейс публичных методов.

  • Паттерн проектирования Абстрактная фабрика
В предыдущем примере был один, но достаточно существенный, недостаток - для расширения функционала нам пришлось править код класса MyArray, Это не всегда допустимо и возможно (если, например, этот класс предоставляется вам сторонней библиотекой или, если этот класс - часть неподдерживаемого кода).
В таком случае можно создать сторонний класс, который будет содержать в себе фабричные методы:
class MyArrayFactory
{
    public MyArray createByDim(int length) {
        return new MyArray(length);
    }
    public MyArray createByDim(int d1, int d2) {
        return new MyMatrix(d1,d2);
    }
    public MyArray createByDim(int[] dims) {
        return new MyArray(dims);
    }
}
А создавать экземпляры MyArray надо теперь так:
var myfactory = new MyArrayFactory();
var a = myfactory.createByDim(10);
var b = myfactory.createByDim(2,5);
var c = myfactory.createByDim(new int[]{ 2, 3, 4 });
Класс, отвечающий за создание объектов других классов, принято называть "фабрикой"

Фабричные паттерны широко используются в библиотеках языка Java

Приведем еще один пример фабрики. Предположим, что от пользователя приходит запрос на объект, реализующий интерфейс ICar. Запрос приходит в виде строки - названия машины:
var factory = new CarFactory();
var c1 = factory.getCar("oka");  // c1 принадлежит классу Oka
var c2 = factory.getCar("niva"); // c2 принадлежит классу Niva
Опишем класс - фабрику машин:
interface ICar {}
class Oka : ICar {}
class Niva : ICar {}

using System;
using System.Collections.Generic;
class CarFactory
{
    private Dictionary<string, Func<ICar>> cars;
    CarFactory() {
        this.cars = new Dictionary<string, Func<ICar>>();
        this.cars.Add("oka",  () => new Oka());
        this.cars.Add("niva", () => new Niva());
    }
    public ICar getCar(string name) {
        if(this.cars.ContainsKey(name)) {
            return this.cars[name]();
        }
    }
}
Обратим внимание, что фабричный метод getCar использует коллекцию-словарь cars, где хранятся пары (строковое имя машины => делегат, возвращающий объект интерфейса ICar). Вызывая делегат по соответствующему ключу, мы получаем требуемый объект (в примере это объекты классов Oka или Niva)

  • Паттерн проектирования Фасад
Предположим, у нас есть написанный в процедурном стиле модуль, который эффективно реализует некоторые матричные операции. Необходимо включить этот модуль к объектно-ориентированному C# коду. Пример такого процедурного модуля:
static class MyLib
{
    public static double[] add(double[] a, double[] b) {...}
    public static double[] mul(double[] a, double b) {...}
}
Пример класса, включающего в себя вызовы методов MyLib
class DoubleVector
{
    public double[] data {get;set;}
    public DoubleVector(int length)
    {
        this.data = new double[length];
    }
    public DoubleVector(params double[] raw)
    {
        this.data = raw;
    }
    public static DoubleVector operator +(DoubleVector a, DoubleVector b)
    {
        return new DoubleVector(MyLib.add(a.data, b.data));
    }
    public static DoubleVector operator *(DoubleVector a, double b)
    {
        return new DoubleVector(MyLib.mul(a.data, b));
    }
}
Теперь вместо явного использования модуля MyLib можно использовать более удобный и объектно-ориентированный класс DoubleVector, Причем все "внутренности" использования MyLib скрыты от пользователя за методами класса DoubleVector. Пример использования DoubleVector:
var a = new DoubleVector(0.0, 1.0, 2.0, 3.0, 4.0);
var b = new DoubleVector(1.1, 2.2, 3.3, 4.4, 5.5);
var c = (a+b) * 0.5;
Сравните:
var a = new double[] { 0.0, 1.0, 2.0, 3.0, 4.0 };
var b = new double[] { 1.1, 2.2, 3.3, 4.4, 5.5};
var c = (MyLib.mul(MyLib.add(a,b), 0.5));
Такой способ создания "обертки" в виде класса над другим классом (осуществляющего, например, более низкоуровневые операции) и называется реализацией паттерна "фасад".
Здесь стоит упомянуть и паттерн адаптер, основным отличием которого от фасада в том, что адаптер является оберткой над несколькими классами. Адаптер может использоваться для создания унифицированного класса, осуществляющего доступ к различным базам данных.

  • Паттерн проектирования Барьер

Last edited Feb 27, 2014 at 11:25 AM by basph, version 33

Comments

No comments yet.