Имя:    Пароль:      Помнить меня       
Unsorted   ~  Software  ~  Development and Design  ~  Объектно-ориентированное программирование проблемы и решения
На страницу «  1, 2, 3
MajorQ
Сообщение  07 Апр 2007, 2:01  Ссылка : Ответить с цитатой
Пол: Мужской  Доверенный пользователь
C нами с 09.06.2005
Репутация: 548.8

Petro, если вместо того, чтобы задействовать эти три идеи, мы начинаем прикручивать некие суррогаты, то это уже не ООП, а онанизм какой-то получается. Смешно Но если их (идеи) можно применить и избежать применения костылей, то почему бы это не сделать? Здравый смысл тут простой: надо делать все в разумные сроки, т.е. стоимость человеко-часов разработки не должна превышать сумму, которую платит заказчик за продукт (а не за идеологию его построения, кстати!).
Хотя, надо все-таки не доводить дело до абсурда и фанатизма, chaser описал вполне жизненную ситуацию (причем, регулярно возникающую). А если все пытаться свести к "чистому" ООП, то результата (продукта) можно и не достигнуть никогда или достигнуть в сроки, за которые конкуренты окучат весь рынок, и остаться с пустым карманом.
В начало
Профиль : Фотоальбом : Блог : Личное Сообщение : Сайт
chaser
Сообщение  07 Апр 2007, 21:58  Ссылка : Ответить с цитатой
Возраст: 35 Пол: Мужской 
C нами с 04.02.2005
Репутация: 121.3

Petro, неважно, на каком языке. Просто это кусок кода, оторванный от контекста, но больше всего он похож на "старое доброе структурное программирование". Массив из структур это еще не ООП Улыбочка

Можно, например, написать такой код:
/* Яблоко, возможно сегментированное */
class Apple
{
public:
  unsigned parts; // количество кусочков
  float part_weight; // вес одного кусочка
};

// далее где-то в коде
void f()
{
    Apple *a = new Apple;
    a->parts = 1; // целое яблоко
    a->part_weight = 150; //весом в 150 грамм

    //... тут что-то делается ...

    if (1 == a->parts) //если яблоко целое...
    {
         a->parts = 4; //разрезать на 4 равных кусочка
         a->part_weight /= 4;
    }

    //...

    // Если голоден и яблоко еще осталось, съесть кусочек
    if (hungry && a->parts)
        if (!(--a->parts)) // если доели яблоко...
        {
            delete a; //удалить его
            a = 0;
        }

    //...
}


Но на ООП это не тянет. Дело в том, что объект должен быть не просто упорядоченным набором данных — он должен проявлять некие способности. Именно этим он отличается от структуры. Например, в данном примере следовало бы реализовать у яблока, как минимум, методы "разрезать на n частей" и "взять кусочек", а также конструктор. И вообще, у объекта не должно быть открытых данных-членов, только если это не примитивный POD-тип.
В начало
Профиль : Фотоальбом : Блог : Личное Сообщение : JabberID
Madlax
Сообщение  07 Апр 2007, 23:48  Ссылка : Ответить с цитатой
 
C нами с 25.01.2005
Репутация: 97.3

как выбрать яблоко среди прочих фруктов ;)
вариант1: шаблон не знаю, наверно, template method

в интерфейс фрукта добавляется метод getFruitLikeThis (извлечь похожий фрукт)

class Fruit
public:
	Fruit* getFruitLikeThis(FruitContainer* list){
		Fruit* item;
		while(item = list->getNextFruit())
			if(item->getType() == this->getType()) return item;
		return 0;
	}

private:
	// Type - что угодно, что можно сравнить
	virtual Type getType() = 0;


клиент использует так

FruitContainer* basket = new FruitContainer;
// ... заполнение контейнера

// образцы для извлечения
Fruit* apple = new Apple;
Fruit* orange = new Orange;

// 2 яблока из корзинки
Fruit* firstAppleFromContainer = apple.getFruitLikeThis(basket);
Fruit* secondAppleFromContainer = apple.getFruitLikeThis(basket);

// апельсин из корзинки
Fruit* orangeFromContainer = orange.getFruitLikeThis(basket);


клиент имеет возможность взять фрукт конкретного типа из контейнера
клиент не знает о том, что есть метод getType()

почему яблоко должно уметь выбирать себя их контейнера?
по той же причине, что треугольник умеет рисовать себя на экране ;)
В начало
Профиль : Личное Сообщение
splav
Сообщение  07 Апр 2007, 23:56  Ссылка : Ответить с цитатой
Возраст: 36 Пол: Мужской  Доверенный пользователь
C нами с 22.01.2006
Репутация: 256.8

Madlax, ну тогда можно и наоборот - сунуть корзине пример и сказать давай мне такой же, т.е. сделать это заботой корзины.
В начало
Профиль : Фотоальбом : Блог : Личное Сообщение : JabberID : ICQ
Madlax
Сообщение  10 Апр 2007, 12:08  Ссылка : Ответить с цитатой
 
C нами с 25.01.2005
Репутация: 97.3

splav, я тоже склоняюсь к тому, что в данном случае лучше сунуть яблоко корзине в качестве образца ;)

первый вариант походит, когда разные фрукты нужно брать из корзинки _по-разному_

еще вариант2: шаблон Visitor

в интерфейс фрукта добавляется виртуальный метод acceptVisitor(FruitVisitor*)

class Fruit
public:
	virtual void acceptVisitor(FruitVisitor*) = 0;


добавляется класс FruitVisitor, который обладает методами для обработки каждого типа фрукта

class FruitVisitor
public:
	virtual void visitApple(Fruit*);
	virtual void visitOrange(Fruit*);


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

от абстрактного FruitVisitor наследуются конкретные посетители для решания разных задач
один конктерный тип посетителя - одна задача

class AppleCollector : public FruitVisitor
public:
	void visitApple(Fruit* fr){
		basket->addFruit(fr);
	}
	void visitOrange(Fruit* fr){
		// ничего собирать не нужно	
	}
	FruitContainer* getCollectedFruits();
private:
	FruitContainer* basket;


аналогично для апельсинов
еще один большой минус, - число классов сборщиков совпадает с числом типов фруктов

class OrangeCollector : public FruitVisitor
public:
	void visitApple(Fruit* fr){
		// ничего собирать не нужно
	}
	void visitOrange(Fruit* fr){
		basket->addFruit(fr);	
	}
	FruitContainer* getCollectedFruits();
private:
	FruitContainer* basket;


для конкретного типа фрукта определяется метод acceptVisitor
название вызываемого метод должно согласовываться с названием класса
class Apple -> visitApple

class Apple
	void acceptVisitor(FruitVisitor* visitor){
		visitor->visitApple(this);
	}


клиент использует так

FruitContainer* basket = new FruitContainer;
// ... заполнение контейнера

FruitVisitor* ac = new AppleCollector;

Fruit* item;
while(item = basket->getNextFruit()){
	item->acceptVisitor(ac);
}

// из коллектора яблок достаются все собранные яблоки
FruitContainer* collectedApples = ac->getCollectedFruits();


этот шаблон очень сильный, т.к. позволяет расписать решение разных задач для структурированной коллекции объектов
В начало
Профиль : Личное Сообщение
chaser
Сообщение  22 Апр 2007, 16:31  Ссылка : Ответить с цитатой
Возраст: 35 Пол: Мужской 
C нами с 04.02.2005
Репутация: 121.3

Проблема вот появилась. Далее предполагается, что используется язык C++.

Есть иерархия контейнеров с абстрактным базовым классом Container, способных хранить объекты одного и того же класса Thing или производных от него.

Так вот, для этих контейнеров нужны однонаправленные итераторы. Ясно, что у разных контейнеров итераторы внутри могут быть устроены совершенно по-разному.

Однако итераторы должны реализовывать общий интерфейс, как и контейнеры. То есть необходима возможность обойти с помощью итератора все элементы контейнера, имея указатель типа Container*.

Функция Container::begin() должна возвращать итератор на начало последовательности. Получается, что сам итератор не может быть полиморфным — мы должны возвращать объект, а не выделенный указатель.

Тогда напрашивается следующее решение. Описывается абстрактный класс iterator_rep (от слова representation), в котором объявляются функции next, equal и get для получения следующего итеатора, сравнения двух итераторов и получения указателя на Thing соответственно.

Класс iterator содержит в себе указатель [или std::auto_ptr] на iterator_rep, и операции ++, ==, *, -> реализуются через соответствующие функции хранимого iterator_rep.

Казалось бы, все хорошо, однако при создании нового итератора приходится динамически выделять память под этот самый iterator_rep (а потом еще освобождать память в деструкторе). Дело в том, что такие контейнеры будут обходиться крайне часто, поэтому эти постоянные выделения-освобождения не очень хорошо скажутся на производительности программы.

Можно предложить решение в стиле C. Класс итератора остается неполиморфным, однако теперь содержит в себе некий указатель void *data. Кроме того, итератор объявлен friend'ом класса Container. Интерфейс, который в предыдущем случае предоставлял класс iterator_rep, теперь будет предоставлять сам Container. Функции next, equal и get будут пользоваться этим самым указателем data, преобразовывая его к нужному типу. Например, если контейнер представляет собой список, data будет указывать на звено списка; если массив — просто на элемент массива. Недостатки тут очевидны, думаю.

Можно, конечно, отказаться от итераторов и объявить в Container'е виртуальную функцию for_each_child, вызывающую переданную ей "callback"-функцию или функциональный объект для каждого своего элемента. Снова есть несколько недостатков. Во-первых, придется реализовывать функции, которые часто будут состоять из одной-двух строк и затруднять чтение программы. Во-вторых, есть более неприятный недостаток. Предположим, что классу Thing в одной из его функций нужно просмотреть элементы некоторого контейнера и для каждого из них вызвать некоторую защищенную функцию-член. С итераторами это не составляет никакой трудности:

for (Container::iterator i = c.begin(); c.end() != i; ++i)
{
    i->some_method();
    //..сделать что-то еще с *i
}


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

В общем, не могу придумать решения, которое мне бы во всем понравилось. Может у кого-нибудь есть идеи?
В начало
Профиль : Фотоальбом : Блог : Личное Сообщение : JabberID
MajorQ
Сообщение  22 Апр 2007, 17:12  Ссылка : Ответить с цитатой
Пол: Мужской  Доверенный пользователь
C нами с 09.06.2005
Репутация: 548.8

chaser писал(а):

Есть иерархия контейнеров с абстрактным базовым классом Container, способных хранить объекты одного и того же класса Thing или производных от него.
...
Функция Container::begin() должна возвращать итератор на начало последовательности. Получается, что сам итератор не может быть полиморфным — мы должны возвращать объект, а не выделенный указатель.

ИМХО это и есть источник всей головной боли. Если объекты в контейнере имеют общего предка, то зачем возвращать именно объект и терять полиморфность содержимого? Явно что-то непроработали!
В начало
Профиль : Фотоальбом : Блог : Личное Сообщение : Сайт
chaser
Сообщение  22 Апр 2007, 18:54  Ссылка : Ответить с цитатой
Возраст: 35 Пол: Мужской 
C нами с 04.02.2005
Репутация: 121.3

Имеется в виду, что возвращаемый итератор должен быть объектом (внутри итератора конечно же лежит указатель на элемент контейнера). Если сделать его указателем, придется ведь возвращать указатель на динамически выделенную для итератора память, которую пользователь нашего итератора должен будет сам удалить. Это неудобно, противоречит принципам ООП и, кроме того, неусточиво по отношению к исключениям:

Container::iterator* i = c.begin();
f(); // f выкидывает исключение, i не будет уничтожен
delete i; 


В варианте с классом iterator_rep мы как раз сохраняем полиморфизм, создавая для выделенного выделенного объекта "умную" оболочку iterator. Однако она мне не нравится благодаря этим самым выделениям памяти.
В начало
Профиль : Фотоальбом : Блог : Личное Сообщение : JabberID
MajorQ
Сообщение  22 Апр 2007, 19:00  Ссылка : Ответить с цитатой
Пол: Мужской  Доверенный пользователь
C нами с 09.06.2005
Репутация: 548.8

Как-то совсем нестандартно, надо сказать... Обычно бывет
Container::iterator i = c.begin();
i->doSmth(); // действо над объектом, указуемым итератором
f(*i); // передача объекта указуемого по ссылке
//...

т.е. используется сам итератор, а не указатель на него...
В начало
Профиль : Фотоальбом : Блог : Личное Сообщение : Сайт
chaser
Сообщение  22 Апр 2007, 19:02  Ссылка : Ответить с цитатой
Возраст: 35 Пол: Мужской 
C нами с 04.02.2005
Репутация: 121.3

Так вот я про то и говорю, что используется сам итератор Улыбочка
То есть мы не можем сам итератор сделать полиморфным (содержащим виртуальные функции).
В начало
Профиль : Фотоальбом : Блог : Личное Сообщение : JabberID
chaser
Сообщение  03 Июн 2007, 18:16  Ссылка : Ответить с цитатой
Возраст: 35 Пол: Мужской 
C нами с 04.02.2005
Репутация: 121.3

В общем, в итоге я отказался от полиморфных контейнеров — не лучшая была идея, надо сказать.
В начало
Профиль : Фотоальбом : Блог : Личное Сообщение : JabberID
DStar17
Сообщение  19 Янв 2012, 19:58  Ссылка : Ответить с цитатой
Пол: Мужской  Доверенный пользователь
C нами с 15.07.2005
Репутация: 133.9

Почему объектно-ориентированное программирование провалилось?
link

_____________________________
С дивана видно всё, ты так и знай!
В начало
Профиль : Фотоальбом : Блог : Личное Сообщение
Dio
Сообщение  19 Янв 2012, 20:07  Ссылка : Ответить с цитатой
Пол: Мужской 
C нами с 16.11.2006
Репутация: 455.1



Собственно, вся суть Улыбочка Прям в точку Улыбочка
В начало
Профиль : Фотоальбом : Блог : Личное Сообщение
Показать сообщения:   
На страницу «  1, 2, 3

Unsorted   ~  Software  ~  Development and Design  ~  Объектно-ориентированное программирование

Ответить на тему

Перейти:  





Powered by phpBB   © Unsorted Team  support@unsorted.me  promo@unsorted.me  Полезные скрипты