Модераторы: LSD, AntonSaburov

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Паттерны в Java и C# 
:(
    Опции темы
Domestic Cat
Дата 4.2.2005, 23:04 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5452
Регистрация: 3.5.2004
Где: Dallas, US

Репутация: 50
Всего: 172



 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.

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


--------------------

PM   Вверх
Domestic Cat
Дата 5.2.2005, 00:46 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5452
Регистрация: 3.5.2004
Где: Dallas, US

Репутация: 50
Всего: 172



&nbsp;Factory
Используется, когда неизвестно, объект какого класса нужно будет создать. В отличие от SimpleFactory в Factory отсутствует явный код выбора класса (как в методе getFilter() в предыдущем случае).
Этот выбор может осуществляться через полиморфизм.
&nbsp;Пример
Есть несколько классов 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() базового класса.
&nbsp;Преимущества.
Фактически, те же, что и у SimpleFactory. Логика получения объектов (ImageFilter) отделена от основного кода.



--------------------

PM   Вверх
Domestic Cat
Дата 5.2.2005, 07:30 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5452
Регистрация: 3.5.2004
Где: Dallas, US

Репутация: 50
Всего: 172



&nbsp;Abstract Factory
Несмотря на название, использовать абстрактный класс в этом паттерне необязательно. Самое главное отличие его от Factory в том, что один класс предоставляет интерфейс для доступа к группе объектов, а не одному объекту.
&nbsp;Пример
Мы пишем игру. Есть класс 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();

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

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


--------------------

PM   Вверх
Domestic Cat
Дата 5.2.2005, 08:31 (ссылка) |    (голосов:2) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5452
Регистрация: 3.5.2004
Где: Dallas, US

Репутация: 50
Всего: 172



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

&nbsp;Пример реализации 1.

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; вместо ретурна нужно бросать иксепшн. Оба метода имеют свои преимущества и недостатки, но они настолько тонкие, что нет смысла о них здесь говорить.

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



--------------------

PM   Вверх
Domestic Cat
Дата 5.2.2005, 23:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5452
Регистрация: 3.5.2004
Где: Dallas, US

Репутация: 50
Всего: 172



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

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

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

&nbsp;Пример
Это очень надуманный пример, поясняющий тем не менее суть паттерна. В роли 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);
 }
}


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



--------------------

PM   Вверх
Domestic Cat
Дата 6.2.2005, 10:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5452
Регистрация: 3.5.2004
Где: Dallas, US

Репутация: 50
Всего: 172



&nbsp;Prototype
Прототип аналогичен паттерну Builder за одним исключением - он создает объекты, клонируя их из некоторого исходного объекта.
Еще раз: Билдер создает объект из множества других; Прототип клонирует объект и преобразовывает его нужным образом.
&nbsp;Пример
Давайте попробуем обойтись без кода.
Предположим, мы пишем АПИ, работающий с БД на низком уровне. Мы создали класс, который может держать таблицу реляционной БД и назвали его, скажем, DataHolder.
Клиент может выполнять запрос и получать в свое распоряжение этот самый объект.
Получает он его естественно, не для просто так, а чтоб поменять, например добавить ряд.
Вопрос: как передать изменения в ДБ? Первый ответ - передать весь DataHolder. Нехорошесть этого подхода следует хотя бы из того, что чаще всего ДБ находится на другом компьютере, т.е. доступ осуществляетя удаленно.
Передавать каждый раз объект, содержащий, возможно, десятки тысяч записей, по сети - не самая хорошая идея, особенно если изменен был всего один ряд.
Выход такой: мы клонируем исходный DataHolder, остатвив в нем только измененые ряды; и передадим его на сервер.
Это и есть паттерн Prototype. Слово "клонируем" не обязательно значит, что нужно пользоваться методом clone() (Clone()). В нашем случае клонировать DataHolder целиком было бы крайтне неэффективно. Скорее, это означает создание своего метода, который бы не только создавал DataHolder но и фильтровал данные оригинального DataHolder, прежде чем скопировать их.

Реализация такого класса (и паттерна) в .NET называется DataSet; описанный паттерн использован в методе GetChanges(). (afair)

&nbsp;Преимущества
Те же, что и у Билдера. Логика создания нового объекта инкапсулирована в реализации Прототипа (т.е. в конкретных методах). Такие методы могут фильтровать, менять исходные данные.


&nbsp;На сем креативные паттерны закончены.


--------------------

PM   Вверх
Domestic Cat
Дата 6.2.2005, 20:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5452
Регистрация: 3.5.2004
Где: Dallas, US

Репутация: 50
Всего: 172



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

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

&nbsp;Примеры
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);
}
}

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


--------------------

PM   Вверх
Domestic Cat
Дата 7.2.2005, 09:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5452
Регистрация: 3.5.2004
Где: Dallas, US

Репутация: 50
Всего: 172



&nbsp;Bridge

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

&nbsp;Пример
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);
}


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


--------------------

PM   Вверх
Domestic Cat
Дата 8.2.2005, 00:30 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5452
Регистрация: 3.5.2004
Где: Dallas, US

Репутация: 50
Всего: 172



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

&nbsp;Пример
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;
  }
//....
}

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


--------------------

PM   Вверх
Domestic Cat
Дата 8.2.2005, 21:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5452
Регистрация: 3.5.2004
Где: Dallas, US

Репутация: 50
Всего: 172



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

&nbsp;Пример
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);
       }
   }
}

&nbsp;Преимущества
Один Декоратор может менять интерфейс для любого количества компонентов.


--------------------

PM   Вверх
Domestic Cat
Дата 10.2.2005, 20:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5452
Регистрация: 3.5.2004
Где: Dallas, US

Репутация: 50
Всего: 172



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

&nbsp;Пример
Чтоб не писать чего-то сложного и нудного, все будет предельно схематично.
Есть классы 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);

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


--------------------

PM   Вверх
Domestic Cat
Дата 13.2.2005, 07:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5452
Регистрация: 3.5.2004
Где: Dallas, US

Репутация: 50
Всего: 172



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

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

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

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


--------------------

PM   Вверх
Domestic Cat
Дата 13.2.2005, 09:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5452
Регистрация: 3.5.2004
Где: Dallas, US

Репутация: 50
Всего: 172



&nbsp;Proxy
Прокси - это объект, который является "представителем" более сложного, трудносоздаваемого объекта. Например, соединение с БД - это пример паттерна Прокси. В каких случаях он нужен?
Нужен он тогда, когда создание объекта занимает много времени (загрузка рисунка, открытие соединения с БД, с удаленным компьютером). Хороший пример - броузер ИЕ, пока рисунок не загружен, рисует стандартнуню иконку с красным квадратиком. Как только загрузка произошла, на этом месте появляется рисунок. Можно легко представить себе как написать такой прокси.
&nbsp;Пример
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());
      }
}


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


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


--------------------

PM   Вверх
Domestic Cat
Дата 16.2.2005, 05:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5452
Регистрация: 3.5.2004
Где: Dallas, US

Репутация: 50
Всего: 172



&nbsp;Chain of Responsibility

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

&nbsp;Пример
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 "кетч-блока". На любом этапе он может быть "пойман" и цепочка прервется.

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


--------------------

PM   Вверх
Domestic Cat
Дата 16.2.2005, 06:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5452
Регистрация: 3.5.2004
Где: Dallas, US

Репутация: 50
Всего: 172



&nbsp;Command

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

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

&nbsp;Пример
С#

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

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. Тогда можно фиксировать выполненные Команды и при необходимости вызывать нужные методы.
Тем не менее, это, имхо, не перевешивает недостатков паттерна.

&nbsp;Преимущества
Решайте сами.


--------------------

PM   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Java"
LSD   AntonSaburov
powerOn   tux
javastic
  • Прежде, чем задать вопрос, прочтите это!
  • Книги по Java собираются здесь.
  • Документация и ресурсы по Java находятся здесь.
  • Используйте теги [code=java][/code] для подсветки кода. Используйтe чекбокс "транслит", если у Вас нет русских шрифтов.
  • Помечайте свой вопрос как решённый, если на него получен ответ. Ссылка "Пометить как решённый" находится над первым постом.
  • Действия модераторов можно обсудить здесь.
  • FAQ раздела лежит здесь.

Если Вам помогли, и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, LSD, AntonSaburov, powerOn, tux, javastic.

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Java: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.1123 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.