Форум программистов
 
Контакты: о проблемах с регистрацией, почтой и по другим вопросам пишите сюда - alarforum@yandex.ru, проверяйте папку спам! Обязательно пройдите активизацию e-mail.

Вернуться   Форум программистов > Технологии > Общие вопросы по программированию, компьютерным наукам
Регистрация

Восстановить пароль
Повторная активизация e-mail

Ответ
 
Опции темы
Старый 12.06.2009, 16:47   #1
atomicxp
Форумчанин
 
Аватар для atomicxp
 
Регистрация: 01.05.2009
Сообщений: 111
Репутация: 63

skype: atomicxp
Вопрос Описание и обсуждение шаблонов проектирования (паттерны)

Как известно для проектирования ООП систем разумно использовать шаблоны проектирования. Существует ряд правил, одно из которых было описано в книге "Совершенный код", например, группировать не более семи элементов в одной сущности (классе).

Однако, помимо общих правил, есть разнообразные модели создания объектов. По сути это важно во всех языках использующих ООП, но меня это больше волнует по части C++ и применительно к кроссплатформенным библиотекам, таким как STL, Boost, GTK+, Qt SDK, WxWidgets и многих других.

По сути во всех этих библиотеках редко соблюдают правила и менее часто соблюдают шаблоны проектирования. Впрочем это не так важно, главное у меня появилась идея обсудить шаблоны проектирования классов/объектов. Пока думаю можно рассмотреть вот эти:

* 1 Структурные паттерны проектирования классов/обьектов
o 1.1 Адаптер (Adapter) - GoF
o 1.2 Декоратор (Decorator) или Оболочка (Wrapper) - GoF
o 1.3 Заместитель (Proxy) или Суррогат (Surrogate) - GoF
o 1.4 Информационный эксперт (Information Expert)- GRASP
o 1.5 Компоновщик (Composite) - GoF
o 1.6 Мост (Bridge), Handle (описатель) или Тело (Body) - GoF
o 1.7 Низкая связанность (Low Coupling) - GRASP
o 1.8 Приспособленец (Flyweight) - GoF
o 1.9 Устойчивый к изменениям (Protected Variations) - GRASP
o 1.10 Фасад (Facade) - GoF
* 2 Паттерны проектирования поведения классов/обьектов
o 2.1 Интерпретатор (Interpreter ) - GoF
o 2.2 Итератор (Iterator) или Курсор (Cursor) - GoF
o 2.3 Команда (Command), Действие (Action) или Транзакция (Транзакция) - GoF
o 2.4 Наблюдатель (Observer), Опубликовать - подписаться (Publish - Subscribe) или Delegation Event Model - GoF
o 2.5 Не разговаривайте с неизвестными (Don't talk to strangers) - GRASP
o 2.6 Посетитель (Visitor) - GoF
o 2.7 Посредник (Mediator) - GoF
o 2.8 Состояние (State) - GoF
o 2.9 Стратегия (Strategy) - GoF
o 2.10 Хранитель (Memento) - GoF
o 2.11 Цепочка обязанностей (Chain of Responsibility) - GoF
o 2.12 Шаблонный метод (Template Method) - GoF
o 2.13 Высокое зацепление (High Cohesion) - GRASP
o 2.14 Контроллер (Controller) - GRASP
o 2.15 Полиморфизм (Polymorphism) - GRASP
o 2.16 Искусственный (Pure Fabrication) - GRASP
o 2.17 Перенаправление (Indirection) - GRASP
* 3 Порождающие паттерны проектирования
o 3.1 Абстрактная фабрика (Abstract Factory, Factory), др. название Инструментарий (Kit) - GoF
o 3.2 Одиночка (Singleton) - GoF
o 3.3 Прототип (Prototype) - GoF
o 3.4 Создатель экземпляров класса (Creator) - GRASP
o 3.5 Строитель (Builder) - GoF
o 3.6 (Фабричный метод) Factory Method или Виртуальный конструктор (Virtual Constructor) - GoF
atomicxp вне форума   Ответить с цитированием
Старый 12.06.2009, 18:25   #2
atomicxp
Форумчанин
 
Аватар для atomicxp
 
Регистрация: 01.05.2009
Сообщений: 111
Репутация: 63

skype: atomicxp
Вопрос Интерфейс

Первый шаблон проектирования для рассмотрения "Интерфейс" (категория: Основные шаблоны (Fundamental)). Следует так же отметить, что открытую часть класса так же называют интерфейсом, но речь пойдёт не о ней. В нашем случае интерфейс может иметь как закрытые, так и открытые члены, плюс некие комбинации, такие как защищённые. Интерфейс не содержит данные, его методы имеют полиморфную природу.

Код:

class InterfaceA
{
 public:
  virtual ma() = 0;
  virtual mb() = 0;
};

virtual указывает на возможность полиморфизма, а = 0 о том что метод имеет чисто абстрактную природу. Однако в вики видим следующую картину.

Код:

class A_Interface
{
 public:
  static A_Interface* create_A();
  virtual ~A_Interface() {}
  virtual do_something() = 0;
};

Код:

class A : public A_Interface
{
 public:
  do_something();
};

Наследование проходит как public, но помимо этого существует статическая функция возвращающая указатель на класс созданный по шаблону проектирования интерфейса. Лично мне это кажется странным, зачем создавать класс A внутри интерфейса. Полиморфизм через интерфейс может достигаться за счёт приведения типов, смысла плодить лишние методы нет.

К тому же это нарушает чистую абстракцию которой по идее должен следовать класс созданный по шаблону проектирования интерфейса.
atomicxp вне форума   Ответить с цитированием
Старый 12.06.2009, 18:58   #3
atomicxp
Форумчанин
 
Аватар для atomicxp
 
Регистрация: 01.05.2009
Сообщений: 111
Репутация: 63

skype: atomicxp
Сообщение Использование интерфейса

Простейший случай использования, который вероятно нужно усовершенствовать:

Код:

class InterfaceA
{
 public:
  virtual void ma() = 0;
  virtual void mb() = 0;
};

class ClassA : public InterfaceA
{
    void ma()
    {
        cout << "ClassA: Method A" << endl;
    }
    void mb()
    {
        cout << "ClassA: Method B" << endl;
    }
};

class ClassB : public InterfaceA
{
    void ma()
    {
        cout << "ClassB: Method A" << endl;
    }
    void mb()
    {
        cout << "ClassB: Method B" << endl;
    }
};

int main()
{
    InterfaceA* interfaceA;

    ClassA* classA = new ClassA();
    interfaceA = classA;
    interfaceA->ma();
    interfaceA->mb();

    ClassB* classB = new ClassB();
    interfaceA = classB;
    interfaceA->ma();
    interfaceA->mb();

    return 0;
}

Вывод:
Код:

ClassA: Method A
ClassA: Method B
ClassB: Method A
ClassB: Method B

Process returned 0 (0x0)   execution time : 0.015 s
Press any key to continue.

atomicxp вне форума   Ответить с цитированием
Старый 12.06.2009, 19:06   #4
pu4koff
Профессионал
 
Аватар для pu4koff
 
Регистрация: 22.05.2007
Сообщений: 9,602
Репутация: 3613
По умолчанию

Цитата:
Сообщение от atomicxp Посмотреть сообщение
Первый шаблон проектирования для рассмотрения "Интерфейс" (категория: Основные шаблоны (Fundamental)).
Не знал, что это шаблон проектирования. Думал всегда, что это "основа" ООП, на ряду с классами. Ну да ладно в общем. Это не суть важно
Цитата:
Сообщение от atomicxp Посмотреть сообщение
В нашем случае интерфейс может иметь как закрытые, так и открытые члены, плюс некие комбинации, такие как защищённые.
Какой же это тогда интерфейс? Кроме того private методы перегрузить не удастся, а это уже будет значит абстрактный класс, а не интерфей.
protected в принципе еще (в случае с С++) допустим. Я посредством них шаблонные методы псевдо-виртуальными делаю
Код:

class IA
{
protected:
  virtual void foo(type_info const &type) = 0;
public:
  template < class T >
  void foo()
  {
    foo(typeid(T));
  }
};

Просто не хочется в коде потом писать все эти typeid. Вызываем как шаблон и получаем полиморфизм. Мне так просто больше нравится в общем
В языках с generic'ами (так сказать, шаблонами времени выполнения), которые позволяют "смастерить" виртуальные "шаблонные" методы, не вижу необходимости в protected методах интерфеев (разве что не хочется лишние сущности плодить и в интерфейс "служебные" методы классов можно "насовать", чтобы конечному пользователю они были не видны, но это всёже несколько нарушает принципы интерфейсов и делает их ближе к абстрактным классам).
Цитата:
Сообщение от atomicxp Посмотреть сообщение
Однако в вики видим следующую картину.
Я почему-то не нашел эту картинув вики и нашлись нормальные примеры. Ну да ладно.
Цитата:
Сообщение от atomicxp Посмотреть сообщение
Наследование проходит как public, но помимо этого существует статическая функция возвращающая указатель на класс созданный по шаблону проектирования интерфейса. Лично мне это кажется странным, зачем создавать класс A внутри интерфейса.
Чушь написал автор кода. Зачем-то на интерфей возложил задачу фабрики. Кроме того это "убивает" все возможные применения интерфея и делает его бесполезным.
Интерфей по определению не должен содержать никакой реализации, а соответственно никаких статиков быть не может. Даже константу в интерфей не стоит совать, не то, что реализовывать какой-то метод.
pu4koff вне форума   Ответить с цитированием
Старый 12.06.2009, 19:10   #5
pu4koff
Профессионал
 
Аватар для pu4koff
 
Регистрация: 22.05.2007
Сообщений: 9,602
Репутация: 3613
По умолчанию

Цитата:
Сообщение от atomicxp Посмотреть сообщение
Простейший случай использования, который вероятно нужно усовершенствовать:
1. Вижу new и не вижу delete
2. Если я не ошибаюсь, то в случае отсутствия деструктора, он создаётся пустой, но не виртуальный. Соответственно, если удалить объект класса ClassA через указатель на интерфейс InterfaceA, то вызовется только деструктор интерфейса, а деструктор класса вызван не будет, соответственно возможно утечка памяти.
Таким образом, я бы в интерфейс добавил еще:
Код:

public:
virtual ~InterfaceA() {};

pu4koff вне форума   Ответить с цитированием
Старый 12.06.2009, 19:48   #6
atomicxp
Форумчанин
 
Аватар для atomicxp
 
Регистрация: 01.05.2009
Сообщений: 111
Репутация: 63

skype: atomicxp
Хорошо

Цитата:
Сообщение от pu4koff Посмотреть сообщение
Какой же это тогда интерфейс? Кроме того private методы перегрузить не удастся, а это уже будет значит абстрактный класс, а не интерфей.
protected в принципе еще (в случае с С++) допустим. Я посредством них шаблонные методы псевдо-виртуальными делаю
Да, это я неправильно написал, надо внимательнее быть, в коде то такое невозможно написать если пишешь интерфейс, ну а на форуме пожалуйста. Вот отсюда был взят пример. Пожалуй следующим вопросом который надо рассмотреть это квалификаторы доступа классу, при наследовании класса, а так же целесообразность использования конструкторов и деструкторов, то есть нужно, не нужно. В следствии чего проверить предположение насчёт утечек памяти.
atomicxp вне форума   Ответить с цитированием
Старый 12.06.2009, 21:12   #7
atomicxp
Форумчанин
 
Аватар для atomicxp
 
Регистрация: 01.05.2009
Сообщений: 111
Репутация: 63

skype: atomicxp
Лампочка Виртуальные деструкторы в интерфейсе

Ну что ж, проблема виртуальных деструкторов рассмотрена. Конечно, данная концепция сильно отличается от C#, хотя при правильном использовании шаблоны проектирования сохраняют своё предназначение, и классам можно смело присваивать стереотип - Interface.

Так же следует отметить, что компилятор подсказывает о том, что нужно записать виртуальный деструктор.
Цитата:
E:\Work\Project\Interface\main.cpp| 6|warning: `class InterfaceA' has virtual functions but non-virtual destructor|
E:\Work\Project\Interface\main.cpp| 18|warning: `class ClassA' has virtual functions but non-virtual destructor|
Были протестированные различные варианты, где при помощи оператора delete вначале удалялся интерфейс, в другом случае класс использующий интерфейс, менялось использование методов, через класс и через интерфейс, и кое-какие другие тесты. Конечный вариант может выглядеть как-то так, хотя здесь нужны изменения.

Код:

#include <iostream>

using namespace std;

class InterfaceA
{
 public:
  virtual void ma() = 0;
  virtual void mb() = 0;
  //~InterfaceA(){ cout << "Desctruction InterfaceA" << endl; }
  // два варианта, виртуальный и не виртуальный деструктор
  virtual ~InterfaceA(){ cout << "Desctruction InterfaceA" << endl; }
  // в случае виртального деструктора при уничтожении интерфейса
  // вызовется уничтожение объекта от наследованного класса
};

class ClassA : public InterfaceA
{
public:
    ~ClassA(){ cout << "Desctruction ClassA" << endl; }
public:
    void ma()
    {
        cout << "ClassA: Method A" << endl;
    }
    void mb()
    {
        cout << "ClassA: Method B" << endl;
    }
};

void Testing(ClassA* classA)
{
    InterfaceA* interfaceA;

    interfaceA = classA;

    interfaceA->ma();
    interfaceA->mb();
}

int main()
{
    ClassA* classA = new ClassA();

    Testing(classA);

    classA->ma();
    classA->mb();

    delete classA;

    return 0;
}

Вывод:
Цитата:
ClassA: Method A
ClassA: Method B
ClassA: Method A
ClassA: Method B
Desctruction ClassA
Desctruction InterfaceA

Process returned 0 (0x0) execution time : 0.015 s
Press any key to continue.
В итоге виртуальный деструктор нужен, если кому-то придёт в голову удалить исходный объект через интерфейс. Хотя такое лучше не делать из-за множественного наследования и в следствии этого потери основного предназначение интерфейса.

Вопрос о квалификаторах доступа пока откладываю на потом в силу того, что для них нужны более совершенные примеры, а нужно ещё рассмотреть множество шаблонов проектирования и их использования применительно к C++ и кроссплатформенному программированию.
atomicxp вне форума   Ответить с цитированием
Старый 13.06.2009, 00:18   #8
alexBlack
Новичок
Профессионал
 
Регистрация: 12.10.2007
Адрес: -
Сообщений: 1,204
Репутация: 920
По умолчанию

А Вам не кажется, что пример несколько вырван из контекста и отсюда путаница.

Одно дело интерфейс как способ описания э... контракта, который должен выполнять некоторый класс и другое дело то понятие, которое рассматривается в статье:

Цитата:
В общем, интерфейс (как шаблон проектирования) — это класс, который обеспечивает программисту простой или более программно-специфический способ доступа к другим классам.
(курсив мой)

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

Исходя из этого и пример к этому шаблону должен быть сделан так, чтобы о классе ClassA упоминания не было.
alexBlack вне форума   Ответить с цитированием
Старый 13.06.2009, 01:09   #9
atomicxp
Форумчанин
 
Аватар для atomicxp
 
Регистрация: 01.05.2009
Сообщений: 111
Репутация: 63

skype: atomicxp
По умолчанию

Цитата:
Сообщение от alexBlack Посмотреть сообщение
В первом случае интерфейс не должен содержать реализации (но это и не шаблон проектирования). А второй позволяет скрыть реализацию класса, реализующего интерфейс (простите за повтор). Отсюда и включение фабрики в шаблон интерфейса. О чем ниже и говорит автор. То есть основное назначение - сокрытие реализации.
Для меня реализация шаблона проектирования интерфейса это прежде всего способ управления другими классами. Если он содержит что-то помимо чисто абстрактных методов, то это не интерфейс.

Насчёт виртуального деструктора согласен, что его надо включить на всякий пожарный, но так же никогда не уничтожать объект при помощи интерфейса. Иными словами он есть, но не должен применяться в тех случаях для которых создан, то есть его как бы для программистов нет.

Во всех остальных случаях, на вроде статических методов, и прочих, их применение будет так же означать, что класс не может ассоциироваться с шаблоном проектирования интерфейса.
atomicxp вне форума   Ответить с цитированием
Старый 13.06.2009, 09:21   #10
pu4koff
Профессионал
 
Аватар для pu4koff
 
Регистрация: 22.05.2007
Сообщений: 9,602
Репутация: 3613
По умолчанию

alexBlack, как интересно только будет выглядеть реализация этого фабричного метода? Класс интерфейса же абстрактный, соответственно создать его никак нельзя. Соответственно, будет строгая зависимость от какого-то другого класса, реализующего этот интерфейс. Таким образом, нет никакой необходимости в абстрактном классе интерфейса и почему бы его сразу не сделать обычным классом и создавать через вызов конструктора, а не через фабричный метод? Ну разве что для тестирования может пригодится (в фабричном методе возвращать какой-то тестовый класс для проверки работоспособности).
Мне кажется, было бы логичнее сделать этот фабричный метод вне класса, чтобы интерфейс уже не зависел от конкретной его реализации.
В общем, как я понял, любая классовая "обёртка" над всякими там API реализует данный паттерн. Тот же MFC получается что является интерфейсом для WinAPI
atomicxp, а почему нельзя удалять через интерфейс то?
Код:

яТранзакция *тр = БД->СтартТранзакции();
яЗапрос *з = тр->СоздатьЗапрос();
...
з->Выполнить();
delete з;
тр->Подтвердить();
delete тр;

Для транзакции еще можно удаление вынести в её подтверждение и откат и это в принципе логично. Но вот запрос где и как удалять? Специальный метод Dispose создавать? Или рядом с фабричным методом метод-щредер создавать? Только и там будет удаление через интерфейс. Или везьде dynamic_cast пихать для удаления?
pu4koff вне форума   Ответить с цитированием
Ответ

Опции темы

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.

Быстрый переход

Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Паттерны проектирования shinauri PHP 0 17.07.2012 17:06
Консольный текстовый редактор и паттерны delias C# (си шарп) 0 22.04.2011 00:41
паттерны для детсада pproger Общие вопросы по программированию, компьютерным наукам 4 11.04.2011 19:40
паттерны проектирования prokach Общие вопросы C/C++ 3 18.01.2011 23:23


15:48.


Powered by vBulletin® Version 3.8.8 Beta 2
Copyright ©2000 - 2018, Jelsoft Enterprises Ltd.

RusProfile.ru


Справочник российских юридических лиц и организаций.
Проекты отопления, пеллетные котлы, бойлеры, радиаторы
интернет магазин respective.ru