Полезное для программистов:

Фриланс
Новости
Статьи
   
Рубрики:


Паттерны в Java и C#

Поиск:
> Simple Factory

Служит для создания объектов различных классов на основании информации, переданной во время выполнения программы. Обычно эти классы наследуют от базового класса или интерфейса, т.е. обладают похожей функциональностью.

В данном случае Фабрика производит объект Filter1 или Filter2 в зависимости от значения переданного в метод getFilter.

Java
Код

class SimpleFactory
{
   
   public static final String FILTER_1 = "filter1";
   public static final String FILTER_2 = "filter2";
   
   public Filter getFilter(String name)
   {
       if (name.equals(FILTER_1))
       {
           return new Filter1();
       }
       if (name.equals(FILTER_2))
       {
           return new Filter2();
       }
       return null;
   }
}

interface Filter
{
   //String filter(String text);
}

class Filter1 implements Filter
{}

class Filter2 implements Filter
{}


C#
Код

class SimpleFactory
{
   public enum Filters
   {
       Filter1,
       Filter2
   }
 
   public IFilter GetFilter(Filters name)
   {
       IFilter f;
       switch (name)
       {
           case Filters.Filter1 :
               f = new Filter1();
               break;
           case Filters.Filter2 :
               f = new Filter2();
               break;
           default:
               f = null;
               break;
       }
       return f;
   }
}

interface IFilter
{
   //String Filter(String text);
}

class Filter1 : IFilter
{}

class Filter2 : IFilter
{}



Паттерн очень простой, потому и не включен в классические 23 паттерна.

Позволяет избавиться от хардкодинга. Например, без этого паттерна нам пришлось бы писать
Код

Filter f = new Filter1();
//....
Filter f2 = new Filter2();
//...
Filter f3;
if (a.equals("filter1"))) f3 = new Filter1();
else //....


Теперь представим что мы решили дать классам Filter<> более осмысленные имена. Нам пришлось бы прочесывать весь код и менять не один раз. С данным паттерном все просто, достаточно изменить класс SimpleFactory.

Паттерн добавляет слой абстракции, отделяя процесс создания объекта от его использования. Фабрика может применять более сложную логику создания объектов; например может создавать их через рефлекшн.
> Factory

Используется, когда неизвестно, объект какого класса нужно будет создать. В отличие от SimpleFactory в Factory отсутствует явный код выбора класса (как в методе getFilter() в предыдущем случае).
Этот выбор может осуществляться через полиморфизм.

Есть несколько классов Filter<>, наследующих от абстрактного Filter. Каждый из фильтров использует свои ImageFilter (и, вероятно еще к-л компоненты).

Java
Код

abstract class Filter
{
   protected ImageFilter imageFilter;
   public ImageFilter getImageFilter()
   {
       return imageFilter;
   }
}

class Filter1 extends Filter
{
   public Filter1()
   {
       imageFilter = new ImageFilter1();
   }
}

class Filter2 extends Filter
{
   public Filter2()
   {
       imageFilter = new ImageFilter2();
   }
}


// ...
Filter f = new Filter1();
ImageFilter imf = f.getImageFilter();


C#
Код

abstract class Filter
{
   protected ImageFilter imageFilter;
   public ImageFilter MyImageFilter
   {
       get
       {
           return imageFilter;
       }
   }
}

class Filter1 : Filter
{
   public Filter1()
   {
       imageFilter = new ImageFilter1();
   }
}

class Filter2 : Filter
{
   public Filter2()
   {
       imageFilter = new ImageFilter2();
   }
}
//...
Filter f = new Filter1();
ImageFilter imf = f.MyImageFilter;

Суть паттерна в том, что обязанность выбора класса ImageFilter ложится на плечи сабклассов. Тем не
менее точка доступа к ImageFilter одна - метод getImageFilter() базового класса.

Фактически, те же, что и у SimpleFactory. Логика получения объектов (ImageFilter) отделена от основного кода.

> Abstract Factory

Несмотря на название, использовать абстрактный класс в этом паттерне необязательно. Самое главное отличие его от Factory в том, что один класс предоставляет интерфейс для доступа к группе объектов, а не одному объекту.

Мы пишем игру. Есть класс Level, от которого наследуют Level1, Level2 и т п. а каждом уровне - свои монстры двух типов - "слабый" и "сильный". Будем считать что все классы <..>Monster наследуют от некого класса Monster.

Java
Код

class Level
{
 protected Monster weakMonster, strongMonster;
 public Monster getWeakMonster()
 {
  return weakMonster;
 }
 public Monster getStrongMonster()
 {
  return strongMonster;
 }
}

class Level1 extends Level
{
 public Level1()
 {
  weakMonster = new AlienMonster();
  strongMonster = new WaterMonster();
 }
}

class Level2 extends Level
{
 public Level2()
 {
  weakMonster = new BadMonster();
  strongMonster = new BigMonster();
 }
}


C#
Код

class Level
{
 protected Monster weakMonster, strongMonster;
 public Monster WeakMonster
 {
  get
  {
   return weakMonster;
  }
 }
 public Monster StrongMonster
 {
  get
  {
   return strongMonster;
  }
 }
}

class Level1 : Level
{
 public Level1()
 {
  weakMonster = new AlienMonster();
  strongMonster = new WaterMonster();
 }
}

class Level2 : Level
{
 public Level2()
 {
  weakMonster = new BadMonster();
  strongMonster = new BigMonster();
 }
}

Здесь класс Level и является Абстрактной Фабрикой, т.к. дает доступ к группе объектов (weakMonster, strongMonster). Теперь можно писать такой код:
Код

Level l = new Level1();
Monster m1 = l.getStrongMonster();
//....
l = new Level2();
Monster m2 = l.getWeakMonster();

Таким образом, основной код понятия не имеет, каких монстров он использует.


Использование Abstract Factory, как и Factory, позволяет писать код, который "не знает" какие конкретно объекты он использует. Наследование позволяет сабклассам самим создавать об3екты, которые им нужны; полиморфизм дает возможность использовать их. Это упрощает как написание, так и поддержку кода, делает его более робастным.
> Singleton

"Одиночка". Паттерн проименяется в случае, когда важно, чтобы в данный момент времени существовал один единственный объект какого-либо класса.
Зачем это нужно?
Предположим мы написали простую базу данных. Наше АПИ включает в себя класс LockManager. Объект этого класса занимается тем, что предотвращает одновременный доступ клиентов к одному и тому же ряду в БД, то есть пока один клиент меняет запись (вызывает метод, передающий изменения), менеджер не позволяет никому другому эту запись изменить.
Некий другой программер решил воспользоваться этим АПИ, и создал 2 объекта LockManager. Теперь возможна ситуация, когда 2 клиента попытаются одновременно менять одну запись. Что может привести к нехорошим последствиям.
Естественно, нужно сделать так, чтобы можно было создать только 1 объект класса LockManager, а при попытке создать большее количество объектов, дать знать программеру об ошибке.
В целом подход один: делаем приватный конструктор, храним инстанс синглтона в самом классе как статик поле; получаем его статик методом типа getInstance().
Есть несколько способов реализации Синглтона.
1. В getInstance() проверяем, если ссылка не нул, возвращаем нул.
2. Если ссылка не нул, кидаем иксепшн.



Java
Код

class Singleton
{
   private static Singleton instance;
   private Singleton() {}
   public static Singleton getInstance()
   {
       if (instance == null)
              instance = new Singleton();
       else return null;
       return instance;
   }

   public static void dispose()
   {
       instance = null;
   }
}


Код на шарпе не привожу, т.к. он на 99% такой же.

Вариант с иксепшном аналогичен; в строчке else return null; вместо ретурна нужно бросать иксепшн. Оба метода имеют свои преимущества и недостатки, но они настолько тонкие, что нет смысла о них здесь говорить.


Дает полный контроль над количеством созданных объектов класса. Паттерн легко применить дкля случая когда нужно чтобы можно было создать не более N объектов; или точно N объектов.

> Builder

Как следует из названия, паттерн собирает ряд объектов в одно целое, "строит" нечто новое.
Сравним паттерн с Factory:
Factory: вы делаете заказ на определенную машину, фабрика ее производит.
Builder: вы даете строителю нужные материалы. Тот что-то из них делает.
Этот паттерн часто используется для построения ГУИ. Например, мы читаем данные из БД, причем для удобства сортировки выносим один столбец со всеми возможными должностями работников отдельно, в виде JList или JComboBox (ListBox, ComboBox). Лист удобен тогда, когда элементов в списке мало, комбобокс - когда их много. Можно легко решить проблему следующим псевдокодом:
Код

класс Строитель
      конструктор (Коллекция)
             если размер Коллекции > х заполнить КомбоБокс
             иначе заполнить Лист

Другим решением было бы создать два класса - строителя, каждый был бы ответственен за построение определенного ГУИ. В приведенном выше случае создание двух классов було бы слишком; но если бы мы решили создать 2 ОЧЕНЬ разных ГУИ, то 2 Билдера было бы решением более "чистым".
Совсем необязательно чтобы Строитель строил визуальные компоненты.


Это очень надуманный пример, поясняющий тем не менее суть паттерна. В роли Builderов выступают ArrayListCollection и ArrayCollection. Каждый создает свой тип коллекции. Для выбора Билдера используется паттерн SimpleFactory (метод GetMyCollection)
C#
Код

 [STAThread]
 static void Main(string[] args)
 {
  Console.WriteLine("1 or 2?");
  int choice = Console.Read() - 49;
  Factory f = new Factory();
  MyCollection mc = f.GetMyCollection(choice);
  mc.Print();
 }
// ....

class Factory
{
 public MyCollection GetMyCollection(int i)
 {
  if (i == 0) return new ArrayCollection(new int[] {1, 3, 5, 7, 8, 9, 3});
  else return new ArrayListCollection(new decimal[] {3M, 6M, 33M, 320M});
 }
}

abstract class MyCollection
{
 public virtual void Print()
 {
  Console.WriteLine("MyCollection: ");
 }
}

class ArrayCollection : MyCollection
{
 public ICollection ic;
 public ArrayCollection(ICollection ic)
 {
  this.ic = ic;
 }
 public override void Print()
 {
  base.Print();
  foreach(object i in ic) Console.Write(" " + i);
 }
}

class ArrayListCollection : MyCollection
{
 private ArrayList aList;
 public ArrayListCollection(ICollection ic)
 {
  aList = new ArrayList();
  aList.AddRange( ic );
 }

 public override void Print()
 {
  base.Print();
  foreach(decimal m in aList) Console.Write(" {0:c}", m);
 }
}



Билдер отделяет логику создания сложных компонентов от кода, использующего эти компоненты. Предположим мы на основе паттерна SimpleFactory выбираем Builder, который строит ГУИ для некого визуального компонента. Такой код легче поддерживать (ГУИ для каждого случая создается в своем классе/методе); легче реюзать; легче дополнять (например, третьим вариантом ГУИ)

> Adapter

Adapter преобразует ("адаптирует") один интерфейс в другой интерфейс.
Может существовать множество причин для такого преобразования, например:
1. Класс А имеет сложный интерфейс, скорее "низкоуровневый". Для многих операций приходится делать несколько вызовов методов этого класса. Имеет смысл переработать этот интерфейс в более простой.
2. Интерфейс класса А очень широк; но для определенных операций нам нужен более узкий, или специализированный интерфейс.
Например, один и тот же класс может использоваься клиентами (удаленно), но может также быть использован администратором сервера. В таком объект не должен "показывать" клиентам всю функциональность.

Есть 2 основных варианта реализации Адаптера: через наследование и через композицию.


Java
Через наследование. Мы написали класс BinaryWriter, который работает с бинарными файлами, при этом шифруя информацию нашим собственным алгоритмом.
Но нам часто приходится писать текстовые данные. Применение Адаптера упрощает работу.

Код

class BinaryWriter
{
public seek(int offset) {...}
public write(byte[] b, int offset) {...}
public write(byte[] b) {...}
.....  
}
class BinaryStringWriter extends BinaryWriter
{
public void append(String s) {...}
}

Теперь мы можем вместо вызовов нескольких методов класса BinaryWriter использовать append.
Но класс BinaryStringWriter по-прежнему обладает всеми паблик методами класса BinaryWriter! Это пример так называемого Two-Way Adapter - двустороннего адаптера, который может выступать как BinaryStringWriter, так и BinaryWriter.

Через композицию.
Класс Account предоставляет интерфейс для управления аккаунтом клиента - снятием денег, удалением аккаунта и пр. Но показывать клиенту определенную функциональност, например метод deleteAccount, не слишком удачный вариант.
Поэтому мы конструируем адаптер - класс ClientAccount, который содержит Account как приватное поле. Класс ClientAccount предоставляет клиенту лишь часть интерфейса этого поля.

Код


class Account
{
public void withdrawMoney(double sum) {...}

public void deleteAccount() {...}
}

class ClientAccount
{
private Account account;
public ClientAccount(Account a)
{
      this.account= a;
}

public void withdrawMoney(double sum)
{
      account.withdrawMoney(sum);
}
}


Адаптер - это что-то вроде "переходника" - он позволяет рассматривать один интерфейс по-разному. Например, можно изменить интерфейс класса JTree, сделав его похожим на интерфейс класса ArrayList или HashMap. Можно переделать интерфейс нескольких совершенно различных классов так, что он будет одним и тем же; такие адаптеры называются Plaggable Adapters. Если подумать, это очень интересная концепция.
> Bridge


Адаптер преобразует интерфейс объекта, оставляя объект "нетронутым". Например, часы в наше время можно засунуть куда угодно, скажем в авторучку или чайник. Но часы останутся по сути своей часами; поменяется их вид и количество кнопочек, и только.
Bridge (мост) делает прямо противоположное - он "фиксирует" интерфейс объекта. Хороший пример Моста - электрическая розетка. Все что нас волнует - это как в нее засунуть вилку и какое там напряжение; сама же электроэнергия может идти с ТЭЦ, ГЭС, АЭС, солнечноей батареи или ветряка - нам все равно. И если завтра вместо ветряка будут пользовать солнечную батарею или белку в колесе, мы этого не заметим.
Часто Мост реализован как связка интерфейс-конкретный класс.


C#
Объект класса Calculator производит некие вычисления в методе Calculate. По завершении метода он вызывает соответствыющие методы всех зарегистрированных слушателей.
Слушатели в свою очередь наследуют от интерфейса IBridge и таким образом гарантированно обладают методом DoCalculationFinished.
Перед вычислением клиентское приложение регистрирует класс слушателя и его метод DoCalculationFinished будет вызван при появлении события CalculationFinished.
Таким образом, работа клиента основана на наличии класса с интерфейсом IBridge, больше его ничто не интересует.
Как показывает пример, сам Мост может делать далее с данными что угодно - слать по сети или сохранять на диск.
Код

class Calculator
{
       private double [] data;
       public delegate Processor (double [] data);
       public event  Processor CalculationFinished;

        // .....

        public void Calculate()
        {
                // ....
                CalculationFinished(data);
        }
}

//...
Calculator c = new Calculator();
IBridge nb = getRegisteredBridge();
c.CalculationFinished += new Processor(nb.DoCalculationFinished);

class NetBridge : IBridge
{
       public void DoCalculationFinished(double[] data)
        {
                // посылаем данные по сети
        }
}

class LocalBridge : IBridge
{
       public void DoCalculationFinished(double[] data)
        {
                // копируем данные в массив-лист, сохраняем как ХМЛ
        }
}

interface IBridge
{
       public void DoCalculationFinished(double[] data);
}



Паттерн сохраняет интерфейс клиента, сохраняя тем самым труд и время программера.
Легко добавить нужную функциональность - например, можно добавить класс PrintBridge, который будет печатать результат, TransmitBridge, который отфильтрует данные и передаст их для дальнейших вычислений, и т п.
> Composite

Обычно мы работаем либо с индивидуальными объектами, либо с коллекциями. Паттерн Composite
объединяет оба случая.
Пример композита - дерево. Дерево имеет листья (листья не могут иметь детей) и ноды, который могут содержать другие ноды или листья.
Суть паттерна в том, чтобы и ноды, и листья имели один интерфейс.


Java
Предположим, что приложение описывает некую иерархическую структуру, например, сотрудников фирмы. Любой сотрудник будет описываться классом, наследующим от AbstractEmployee, т.е. интерфейс у всех классов будет один.
AbstractEmployee по сути есть часть древовидной структуры, и хранит ссылку на родителя и детей.
Сабклассы AbstractEmployee по-своему имплементруют соответствующие методы; так например класс Employee (обычный сотрудник) на дереве будет листом, т.е. getSubordinates должен возвращать null.

Код

аbstract class AbstractEmployee
{
    public abstract void addEmployee(AbstractEmployee ae);    // добавить подчиненного
    public abstract void deleteEmployee(AbstractEmployee ae);
    public abstract double getSalaryEmployee();
    public abstract double setSalary(double s); // установить зарплату
    public abstract boolean isLeaf(); // труе если это сотрудник без подчиненных
    public abstract Iterator getSubordinates(); // получить итератор коллекции подчиненных
    public AbstractEmployee getParent(); // получить ссылку на начальника
    public abstract String getName();
}

class Employee extends AbstractEmployee
{
// имплементация интерфейса
 // ....
  public Iterator getSubordinates()
  {
        return null;
  }
  public boolean isLeaf()
  {
        return true;
  }
//....
}

class Manager extends AbstractEmployee
{
   ArrayList children;
// имплементация интерфейса
 // ....
  public Iterator getSubordinates()
  {
        return children.iterator();
  }
  public boolean isLeaf()
  {
        return false;
  }
//....
}


Этот паттерн широко используется в Java и C#. Например, любая JPanel (Panel) содержит ссылки на свои компоненты (которые в свою очередь сами держат ссылки на их компоненты). Поскольку компоненты наследуют от Component (Control) то и интерфейс у них один.
Композит позволяет упростить работу с похожими объектами.
> Decorator

Декоратор похож на Адаптер в том смысле, что он преобразует интерфейс исходного класса. Однако, как и в случае с многими паттернами, цель Декоратора другая.
Его задача - изменить интерфейс нескольких классов. Адаптер удобно использовать, делая его сабклассом нужного класса. А если нужно 20 классов "привести" к одному интерфейсу? Нужно создавать 20 сабклассов.
Другой момент - Декоратор часто используется в ГУИ.
Суть паттерна в том, что создается класс, содержащий поле; класс этого поля является суперклассом (или интерфейсом) для тех классов, которые нам нужно "продекорировать".


Java
Данный пример декорирует JPanel - когда указатель мыши находится над панелью, она обводится красной рамкой. В Декоратор мы помещаем не JPanel а JComponent; поэтому его можно использовать для декорации не только панели.

Код

import java.awt.event.*;
import javax.swing.*;
import java.awt.*;

public class test extends JFrame
{
   test()
   {
       setSize(300, 300);
       JPanel p = new JPanel();
       JPanel p1 = new JPanel();
       p1.add(new JLabel(" * "));
       p.add(new SuperDecorator(p1));
       getContentPane().add(p, BorderLayout.CENTER);
       setDefaultCloseOperation(EXIT_ON_CLOSE);
       setVisible(true);
   }
   public static void main(String[] args)
   {
       new test();
   }
}

class Decorator extends JComponent
{
   public Decorator(JComponent c)
   {
       setLayout(new BorderLayout());
       add("Center", c);
   }
}

class SuperDecorator extends Decorator
{
   private boolean isMouseOver;
   private JComponent c;
   
   public SuperDecorator(JComponent com)
   {
       super(com);
       c = com;
       MyAdapter ma = new MyAdapter();
       c.addMouseListener(ma);
   }
   
   class MyAdapter extends MouseAdapter
   {
       public void mouseEntered(MouseEvent me)
       {
           isMouseOver = true;
           repaint();
       }
       public void mouseExited(MouseEvent me)
       {
           isMouseOver = false;
           repaint();
       }
   }
   
   public void paint(Graphics g)
   {
       super.paint(g);
       if (isMouseOver)
       {
           Dimension size = super.getSize();
           g.setColor(Color.RED);
           g.drawRect(0, 0, size.width-1, size.height-1);
       }
   }
}


Один Декоратор может менять интерфейс для любого количества компонентов.
> Facade

Что (отчасти) неудобно в ООП и при использовании паттернов - большое количество классов.
Чем больше классов - тем сложнее работать. Фасад - это класс, который предоставляет простой интерфейс для работы, и прячет таким образом сложную бизнес логику приложения.
Естественно, упрощение интерфейса приводит к потере гибкости при работе с Фасадом. Тем не менее, программист по-прежнему имеет доступ к "низкоуровневому" АПИ. Фасад - всего лишь класс, который упрощает жизнь в некоторых (или многих) случаях.
Можно использовать несколько Фасадов, наследующх от одного класса.


Чтоб не писать чего-то сложного и нудного, все будет предельно схематично.
Есть классы A, B, C. Каждый раз нам приходится визывать ряд методов этих классов:
Код

class A
{
     public void MethodA() { ... }
}
class B
{
     public C MethodB1() { ... }
     public void MethodB2() { ... }
}
class C
{
     public void MethodC1() { ... }
     public void MethodC2() { ... }
}

//...
A a = new A();
B b = new B();
//...

a.MethodA();  // Блок 1
C c = b.MethodB1();
c.MethodC2();
// работаем с c
//.....
c.MethodC1(); // Блок 2
b.MethodB2();
c.Dispose();


Создадим класс Facade, в который вынесем Блок 1 и Блок 2 :

Код

public class Facade
{
     public Facade()
     {
            A a = new A();
            B b = new B();
     }
     public C DoFirstBlock()
     { Блок 1 }

     public void DoSecondBlock(C c)
     { Блок 2}
}

Теперь все упрощается
Код

Facade facade = new Facade();
C c = facade.DoFirstBlock();
//...
facade.DoSecondBlock(c);


Упрощает интерфейс. Клиенту теперь не нужно знать тонкостей работы с A, B и C. ВСе взаимодействие с кэтими классами ограничено двумя методами. С другой стороны, если придется поменять логику взаимодействия этих классов, вероятно, придется менять Фасад, или снова пользоваться исходными методами.
> Flyweight

Объектно-ориентированный подход подразумевает создание классов, и естественно, объектов этих классов. Иногда нужно создать множество объектов одного класса. Если объекты отличаются только несколькими параметрами, применяется паттерн Flyweight.
Рассмотрим классический пример - папки на диске. У каждой папки свое название и иконка. Естественно, создавать свой объект для каждой папки - значит зодать множество почти одинаковых объектов.
Паттерн Flyweight означает следующее: нужно написать один класс, создать один объект этого класса, и в аргументы соответствующих методов передавать нужные параметры (название и имадж папки), а не создавать свой объект для каждой. Что-то вроде такого:
Код

class Folder
{
  // ....
  public void draw(Graphics g, String folderName, Image image, Rectangle bounds)
  {
     // ....

Flyweight используется нечасто, потому весь пример я опущу (он очевиден, к тому же). Но зато существует несколько разновидностей паттерна, одна из которых


Пусть у нас есть класс, объекты которого получаются очень большими. Тогда можно пользовать паттерн Sharable, который по сути есть Flyweight , но намерение другое - предоставить один и тот же объект для различных клиентов.
> Proxy

Прокси - это объект, который является "представителем" более сложного, трудносоздаваемого объекта. Например, соединение с БД - это пример паттерна Прокси. В каких случаях он нужен?
Нужен он тогда, когда создание объекта занимает много времени (загрузка рисунка, открытие соединения с БД, с удаленным компьютером). Хороший пример - броузер ИЕ, пока рисунок не загружен, рисует стандартнуню иконку с красным квадратиком. Как только загрузка произошла, на этом месте появляется рисунок. Можно легко представить себе как написать такой прокси.

Java
Код неполный, но дописать легко.
Код

public class Calculator
{
       public boolean done;
       private String result;
       public void calculate()
       {
             // супердолгие вычисления  
              done = true;
       }
        public String getResult()
        {
               return result;
        }
}

public class CalculatorProxy
{

        private Calculator calculator;
        public CalculatorProxy()
        {
               calculator = new Calculator();
               calculator.calculate();
        }
        public String getResult()
        {
              if (calculator.done)
                      return calculator.getResult();
              else return "Still calculating, sir";
        }
}

public class Test
{
      public static void main(String[] args)
      {
             CalculatorProxy cp = new CalculatorProxy();
              while(true)
                       System.out.println(cp.getResult());
      }
}



Прокси упрощает работу с объектами, на взаимодействие с которыми может уйти много времени. В зависимости от задачи, прокси может выполнять функции кеширования, преобразования объекта.


 На этом структурные паттерны закончены.

> Chain of Responsibility


Как и многие другие паттерны, предназначен для того, чтобы объекты как можно меньше знали друг о друге.
Пусть нам нужно обработать какое-либо событие, мессадж, попросту говоря, какой-либо объект. Смысл паттерна в том, чтобы передавать его от объекта к объекту в надежде, что кто-то да будет знать что с ним делать.
Это очень напоминает несколько catch блоков - они идут от наиболее специфического к наиболее общему; нет гарантии что на выходе объект все-таки будет "пойман"; процессинг выполняетя последовательно; кетч-блоки ничего не знают друг о друге.


Java


Код

import java.util.*;

public class Test
{
public static void main(String[] args)
{
   Chain chain = new Chain();
   chain.addProcessor(new DogProcessor());
   chain.addProcessor(new LengthProcessor());
   chain.process("Quick cat slow dog");
   chain.process("Toooooooooooooooo looooooooooooooooong");
}
}

interface Processor
{
boolean process(String msg);
}

class Chain implements Processor
{
protected ArrayList<Processor> aList = new ArrayList<Processor>();

public void addProcessor(Processor p)
{
   aList.add(p);
}

public boolean process(String msg)
{
   for (int i = 0; i < aList.size(); i++)
   {
    if (aList.get(i).process(msg)) return true;
   }
   return false;
}
}

class DogProcessor implements Processor
{
public boolean process(String msg)
{
   if (msg.indexOf("dog") != -1)
   {
    System.out.println("Dog found!!! Process stopped");
    return true;
   }  
   return false;
}
}

class LengthProcessor implements Processor
{
public boolean process(String msg)
{
   if (msg.length() > 25)
   {
    System.out.println("Too long!!! Process stopped");
    return true;
   }  
   return false;
}
}


Пример демонстрирует суть паттерна - мессаге - это "иксепшн", который прогоняется через 2 "кетч-блока". На любом этапе он может быть "пойман" и цепочка прервется.


Уменьшает связь между элементами цепи; таким образом приводит к структурированному, легко поддерживаемому и раширяемому коду.
> Command


Не понимаю я Комманд паттерн. Точнее понимаю, но зачем он нужен - непонятно. Поскольку он обычно применяется в ГУИ (по крайней мере в книжках), рассмотрим и мы его в таком контексте.

Итак, есть фрейм (форма), на которой находится 20 кнопок. Вопрос: где обрабатывать нажатие этих 20 кнопок? Вариант 1: добавляем 20 листенеров (делегатов), все в классе фрейма (формы). Выглядит это не очень красиво, да и класс разрастается хуже некуда - 20 методов.
Вариант 2 - все кнопки привязываем к одному методу, где отлавливаем источник нажатия и выполняем нужные действия. Этот вариант еще хуже, т.к. мы получим один гигантский метод-переросток с 20 ifами.
Вариант номер 3 - сделать отдельный класс-делегат с 20 методами-слушателями. Результат тоже нехороший - отдельный, фактически ненужный класс.
Здесь вроде бы и помогает Комманд.


С#

Для начала пишем интерфейс
Код

public interface Command
{
       void Execute();
}


Теперь добавляем в класс формы единственный метод - делегат
Код

//...
public void command(object sender, EventArgs e)
{
       ((Command) sender).Execute();
}


Прикол весь в том, что теперь каждый баттон должен наследовать от интерфейса Command. То есть, нужно написать для каздой кнопки сабкласс от Button
Код

public class Button1 : Button,  Command
{
       public void Execute() { /* Тут делаем чего-нибудь */ }
}


Если все 20 кнопок делают чего-то свое, то нужно либо 20 сабклассов, либо 1 сабкласс, причем в Execute будут намешаны 20 ifов...

Проблема в том, что мы этим ничего не выиграли, кроме того, что код стал "более правильным". Каждая кнопка отвечает сама за себя. Но работы гораздо больше - сколько сабклассов нужно написать! Учтите еще, что теперь просто так бросать кнопки на форму не получится - нужно написать UserControl и поместить его на тулбокс; и лишь затем делать гуи.

Более ясной становится полезность паттерна если учесть, что в Command можно добавить другие методы, типа Undo / Redo. Тогда можно фиксировать выполненные Команды и при необходимости вызывать нужные методы.
Тем не менее, это, имхо, не перевешивает недостатков паттерна.


Решайте сами.
> Interpreter


Интерпретатор есть интерпретатор. Нужен он если
1. Необходимо парсить алгебраические выражения (см Origin, Mathematica, Matcad и пр.).
2. Необходимо "понимать" команды пользователя или выполнять определенные сценарии. Пример - скрипты в играх.
3. Нужно менять вывод данных, например, парсить команду юзера и выдавать необходимые столбцы БД, отсортированные по к-л признаку.

Вот собственно, и все. Примеров здесь не будет, так как их можно найти где угодно, в том же Страуструпе, и переделать под нужный язык.
> Iterator

Позволяет скрыть внутреннюю структуру и бизнес логику коллекции данных, предоставляя стандартный интерфейс для клиента. Думаю, тут особо объяснять не нужно, так как итераторы являются неотъемлимой частью Java и C#.
В Java есть интерфейс Iterator с методами hasNext, next, remove; C# - IEnumerator (Move, Reset, Current). Кроме того, в шарпе есть также индексеры, идея которых та же.

Java

Без комментариев, все очевидно.
Код
import java.util.*;

public class Test
{
 public static void main(String[] args)
 {
    Employees e = new Employees();
    Iterator i = e.iterator();
    while(i.hasNext())
    {
     System.out.println(i.next());
    }
 }
}

class Employees
{
private String[] data;
private EmployeeIterator iter;
Employees()
{
 data = new String[] {"John Smith", "Pablo Picasso", "Stephen Spielberg"};
}

public Iterator iterator()
{
                // lazy evaluation
 if (iter == null)
  iter = new EmployeeIterator();
 else
  iter.reset();
 return iter;
}

public class EmployeeIterator implements Iterator
{
 private int index;
 
 public boolean hasNext()
 {
  return index < data.length;
 }
 
 public Object next()
 {
  index++;
  return data[index - 1];
 }
 
 public void remove() throws UnsupportedOperationException
 {
                       // птичка Обломинго - нема у нас такого метода
  throw new UnsupportedOperationException();
 }
 
 public void reset()
 {
  index = 0;
 }  
}
}




Предоставляет стандартный интерфейс, чем и напоминает паттерн Bridge. Преимущества те же - реализация скрыта от клиента.
> State

Предположим, что нужно создать некий объект, который будет вести себя по-разному, в зависимости от его текущего состояния. Примером может служить объект типа логгер, который можно "переключать" между состоянием "писать в файл" и "выводить на консоль".
Нетрудно догадаться, что реализация такого объекта включала бы множество конструкций if-else, что-то вроде
Код

if (currentState.equals("toFile"))
     // writeToFile()
else
if (currentState.equals("toConsole"))
   // writeToConsole()
else
//.........

Чем больше возможный состояний, тем сложнее становится класс, тем труднее его менять и поддерживать.
Чтобы исправить ситуацию, используем State паттерн. Вот одна из возможных реализаций :
1. Пишем абстрактный класс State с нужными нам методами
2. Пишем конкретные сабклассы State с реализацией этих методов.
3. Пишем класс, содержащий указатель на текущий сабкласс State.

Для нашего случая (самого элементарного) логгера см. пример.


Java

Код
import java.util.*;
import java.io.*;

public class Test
{
   public static void main(String [] ar) throws Exception
   {
      LogManager lm = new LogManager();
      lm.setToFile();
      lm.log("Hello!");
      lm.setToConsole();
      lm.log("I'm a log manager");
      lm.dispose();
   }
}

class LogManager
{
private State currentState;
private ConsoleState cs = new ConsoleState();
private FileState st = new FileState();

public void setToConsole()
{
 currentState = cs;
}

public void setToFile()
{
 currentState = st;
}

public void log(String s)
{
 currentState.log(s);
}

public void dispose()
{
 st.dispose();
}
}

abstract class State
{
public abstract void log(String s);
public void dispose() {};
}

class ConsoleState extends State
{
public void log(String s)
{
 System.out.println(new Date().toString() + "   " + s);
}
}

class FileState extends State
{
private FileWriter filew;
public FileState()
{
 try
 {
  filew = new FileWriter("log.txt");
 }
 catch (IOException ioe)
 {
  ioe.printStackTrace();
 }
}

public void log(String s)
{
 try
 {
  filew.write(new Date().toString() + "   " + s);
  filew.flush();
 }
 catch (IOException ioe)
 {
  ioe.printStackTrace();
 }
}

public void dispose()
{
 try
 {
  filew.close();
 }
 catch (IOException e)
 {
  e.printStackTrace();
 }
}
}

> Strategy

Strategy паттерн очень похож на State, но назначение у него иное. State паттерн означает что возможно переключение между состояниями, поэтому состояния сосуществуют. Strategy, как следует из названия, - это выбор стратегии поведения, алгоритма вычислений. Алгоритм выыбирается и используется чаще всего один раз, ингда даже не пользователем а автоматически.
Примером является сохранение текста в редакторе - его можно сохранить в разных форматах - то есть, нужно выбрать алгоритм записи текста на диск.

Различие между паттернами довольно тонкое - то, что в одной ситуации может выглядеть как "состояние", в другой окажется "стратегией". Все зависит от намерений программиста.


C#
Код

namespace ConsoleApplication1
{
class Class1
{
 [STAThread]
 static void Main(string[] args)
 {
  Console.WriteLine("Enter 1 or 2");
  Strategy strategy = new Strategy();
         string s = Console.ReadLine();
  if (s == "1")
   strategy.SetToSqr();
  else
   strategy.SetToSqrt();
  strategy.DoOutput();
 }
}

internal class Strategy
{
 private Output activeStrategy;
 
 public void SetToSqrt()
 {
  activeStrategy = new SqrtOutput();
 }

 public void SetToSqr()
 {
  activeStrategy = new SqrOutput();
 }

 public void DoOutput()
 {
  activeStrategy.DoOutput();
 }
}

internal abstract class Output
{
 internal abstract void DoOutput();
}

internal class SqrOutput : Output
{
 internal override void DoOutput()
 {
  for (int i = 0; i < 10; i++)
   Console.WriteLine(i + "*" + i + " = " + i*i);
 }
}
internal class SqrtOutput : Output
{
 internal override void DoOutput()
 {
  for (int i = 0; i < 10; i++)
   Console.WriteLine("sqrt(" + i + ") = " + Math.Sqrt(i));
 }
}
}

Автор: Domestic Cat
Сайт: http://<a href="http://java.sun.com">www.guga.ru</a






Просмотров: 23673

 

 

Новые статьи:


Популярные:
  1. Как сделать цикличным проигрывание MIDI-файла?
  2. Создание AVI файла из рисунков
  3. Как устройство "отключить в данной конфигурации"?
  4. Kто в данный момент присоединен через Сеть?
  5. Как узнать количество доступной памяти?
  6. Как реализовать в RichEdit разноцветный текст?
  7. Как скрыть свое приложение от ProcessViewer
  8. Как программно нажать/скрыть/показ кнопку "Start"?
  9. Модуль работы с ресурсами в PE файлах
10. Функции вызова диалоговых окон выбора
11. Проверка граматики средствами Word'а из Delphi.
12. Модуль для упрощенного вызова сообщений
13. Функции для записи и чтение своих данных в, ЕХЕ- файле
14. Рекурсивный просмотр директорий
15. Network Traffic Monitor
16. Разные модули
17. Универсальная функция для обращения к любым экспортируем функциям DLL
18. Библиотека от VladS
19. Протектор для UPX'а
20. Еще об ICQ, сообщения по контакт листу?
21. Использование открытых интерфейсов
22. Теория и практика использования RTTI
23. Работа с TApplication
24. Примеры использования Drag and Drop для различных визуальных компонентов
25. Что такое порт? Правила для работы с портами
26. Симфония на клавиатуре
27. Загрузка DLL
28. Исправление автоинкремента
29. Взаимодействие с чужими окнами
30. Проверить дубляжи в столбце


 

 

 
 
На главную