LINUX.ORG.RU

Как правильно создавать сложные объекты?

 ,


0

2

Предположим есть объект с достаточно большим количеством полей. Причём, для объекта они важны (почти) все, значений по-умолчанию у них нет и они зависят друг от друга (например, дата изменения файла должны быть не меньше даты создания и т.д.).

Как правильно написать конструктор такого объекта? Вроде как банально new myItem(f1, f2, f3, ..., f32) некрасиво. В голову приходит только что-то вроде

i = new myItem();
i.f1 = ...;
i.f2 = ...;
...
i.f32 = ...;
i.init(); // здесь проверяется корректность данных

но это как-то совсем громоздко. Как-то красивее можно?

★★★★★

Последнее исправление: monk (всего исправлений: 1)

Ответ на: комментарий от anonymous

Представьте себе что мы хотим добавить метод setPrice, например. Все приехали, Вам придется переписывать все билдеры.

/** "Abstract Builder" */
abstract class PizzaBuilder

/** "ConcreteBuilder" */
class HawaiianPizzaBuilder extends PizzaBuilder

/** "ConcreteBuilder" */
class SpicyPizzaBuilder extends PizzaBuilder

Зачем переписывать все билдеры? Что ты предлагаешь взамен?

winlook38 ★★
()
Ответ на: комментарий от winlook38

Смысла в мутабельном отчете тоже немного

Смысла немного, но и делать его специально иммутабельным, ради того *шоб було*, тоже никакого нет, поэтому и профита ни от иммутабельности ни от билдера тут тоже нет.

anonymous
()
Ответ на: комментарий от anonymous

делать его специально мутабельным, ради того *шоб було*, тоже никакого нет

winlook38 ★★
()
Ответ на: комментарий от anonymous

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

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

Зачем вы применяете паттерн строителя, до того как построили модель в будущей программе?

Представьте себе что мы хотим добавить метод setPrice

Вы не поняли сути паттерна, последняя попытка объяснить досконально. Строитель - понимаете вы - он строит, просто строит. Вот вы наняли строителя - (сейчас будет суть паттерна, внимание!) построить вам дом. Сами проект для него делали, все мелочи продумали, но в плане забыли добавить дверь! Оп-ля, прям вот такой поворот, да. И после этого, хотите добавить эту дверь (ваш setPrice). Но уже после того, как строитель выполнил ваш план. Дом уже стоит. И кто виноват - проектировщик, или строитель? То-то же.

Как вам донести что этот паттерн, суть, строит конечное представление продукта из тех материалов, и ТАК (прочитайте внимательно еще раз - ТАК) как вы планировали изначально! Если вы в данном конкретном примере про пиццу хотите добавить метод setPrice - вы изменяете условие ТАК. Сл-но, хотите строить продукт уже НЕ ТАК, как планировали изначально. Конкретно для вашего примера, вы добавляете новую часть к конечному продукту - ну дак и делайте новое представление, ведь изначально, при проектировании setPrice не подразумевался, раз вы уже написали кучу других строителей. Я исхожу из логики вашего примера сейчас, про пиццу.

И не нужно из-за этого сетовать на паттерн, только потому что вы не можете понять его сути. Он здесь не причем, поймите вы уже это. Паттерн строитель применяют во многих крупных масштабируемых проектах. Вспомните design patterns, если когда-либо читали, там есть примеры конкретно для строителя. И да, побольше читайте википедию. Да хотя дело даже не в этом, тот пример предполагает, что модель пиццы уже полностью спроектирована, и конкретный продукт, который они там собирают, они будут уже использовать, понимаете. Если им, именно в этом представлении понадобится добавить новую часть (setPrice) - это проблема неправильного проектирования, но не проблема паттерна.

Как правильно написали мне выше

Лучше давать примеры, когда формируемый Строителем объект иммутабелен, так понятнее и логичнее, ведь Строитель формирует представление каких-то данных.

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

И бога ради, простите, если где-то, по ходу чтения, уловите мои неадекватные нотки, я не со зла, просто дико устал объяснять :(

znenyegvkby
()
Ответ на: комментарий от znenyegvkby

Я не понимаю что вы хотите мне донести. Мы с вами уже 3 раза это обсудили

Ты ведь не в курсе, кто такой анонiмус, да?

anonymous
()

Объект сам должен проверять свою валидность, либо при каждом изменении состояния либо отдельным методом.

TDrive ★★★★★
()
Ответ на: комментарий от znenyegvkby

Мы с вами уже 3 раза это обсудили

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

Pizza := Object clone do(
   dough ::= ""; sauce ::= ""; topping ::= ""
)

HawaiianPizza := Pizza clone setDough("cross") setSauce("mild") setTopping("ham+pineapple")
SpicyPizza := Pizza clone setDough("pan backet") setSauce("hot") setTopping("pepperoni+salami")

Waiter := Object clone do(
  getPizza := method(pizza, pizza clone)
)

waiter := Waiter clone

waiter getPizza(HawaiianPizza) dough print // cross
Даже если забить на расширяемость, достаточно того, что это на хрен не нужное дерьмо.

anonymous
()
Ответ на: комментарий от Oxdeadbeef

Затем, чтобы нормальную работу получить, а не быть всю жизнь «суперджедаем» (С)(TM) (эникейщиком).

Bioreactor ★★★★★
()

сложное состоит из простого, например узел дерева

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

Больше формализаций и абстракций. Например сложное в классе обозначить полем complexity

anonymous
()
Ответ на: комментарий от Bioreactor

Учите шаблон IoC.

Улыбнуло рассуждение одной из мартышек разглагольствующих о полезности IoC

http://habrahabr.ru/post/131993/

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

Почему то она не упоминает о том, что подобные проблемы возникают только в недоязычках вроде жабы, в ООП-языках изменив реализацию менеджера нам не придется менять реализацию вьюера. Возможно она об этом не догадывается, а возможно, скромно умалчивает, так или иначе, бросается героически решать жабапроблемы внедрением слабых говнозависимостей. Разве не забавно? И сколько слов новых получается, будет выглядеть умной, работодателям это нравится.

anonymous
()
Ответ на: комментарий от peregrine

А вообще раз такое получилось, то у тебя что-то не то в логике

Может. Пусть, например, надо описать в программе стеклопакет как товар. У него есть куча размеров (десятка два). Эти размеры не произвольные (не может быть толщина рамки больше высоты окна). Нулевые размеры запрещены.

В интерфейсе эти данные все в разных полях ввода и есть кнопка «сохранить», которая должна создать объект. Как правильно написать обработчик для этой кнопки?

monk ★★★★★
() автор топика
Ответ на: комментарий от blexey

Поскольку при первом вызове .setpoint(int num, double x, double y) у него есть достаточно информации, чтобы определить, что никаких двух вершин для этой проверки пока не существует

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

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

все ясно - рекомендуется переосмыслить книжку GoF - там примерчик про шасси

anonymous
()
Ответ на: комментарий от anonymous

Почему то она не упоминает о том, что подобные проблемы возникают только в недоязычках вроде жабы, в ООП-языках изменив реализацию менеджера нам не придется менять реализацию вьюера.

Давай, мартышка, выкатывай ссылку на труЪ проект на Io, который не просто хомепага 7классника с синдромом дауна. Если у тебя нет такого проекта, GTFO отсюда, ибо ты — бесполезное и никчемное трепло, которое не понимает разницу между кукарекингом и практикой. Иди Декорте поучи писать на io, скажи ему, что обжСи есть говно и что Декорте макак, лол.

anonymous
()
Ответ на: комментарий от monk

entity как товар проще описывать просто набором свойств, делаешь GADT с typelevel списком имя свойства - тип свойства, по этой информации компилятор тебе делает код, который генерирует форму (если не хватает информации добавляешь ещё пару типов), по этой же форме у тебя генерируется обработчик запроса с результатами формы, далее пишешь валидатор:

validate :: HList (xs :: [(Literal,*)]) -> (HList xs -> a) -> [(HList xs -> Maybe String)] -> Either [String] a

и своё окошко делаешь newtype враппером над списком свойств.

qnikst ★★★★★
()
Ответ на: комментарий от monk

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

В данном языке при наличии ООП не существует возможности использовать динамические структуры данных или указать отсутствие значения каким-то иным способом, нежели одним из значений?
Либо это какой-то неправильный язык, либо ты напрасно смешиваешь в нём понятие отсутствия сущности с нулями и прочими значениями.

blexey ★★★★★
()
Ответ на: комментарий от qnikst

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

А генератор тоже самому писать? Или есть штатный генератор страниц/парсеров для GUI и HTML?

monk ★★★★★
() автор топика
Последнее исправление: monk (всего исправлений: 1)
Ответ на: комментарий от blexey

В данном языке при наличии ООП не существует возможности использовать динамические структуры данных или указать отсутствие значения каким-то иным способом, нежели одним из значений

То есть вместо

class Twenty 
{
   double x[20];
   double y[20];
   ...
}

ты предлагаешь делать

struct double_or_null
{
   boolean null;
   double val;
}

class Twenty 
{
   double_or_null x[20];
   double_or_null y[20];
   ...
}

или

class Twenty 
{
   vector<double> x;
   vector<double> y;
   ...
}
?

Тогда уж проще один флажок на весь объект + метод init().

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

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

qnikst ★★★★★
()
Ответ на: комментарий от monk

в явах и т.п. вроде канонично было делать Builder (имеющий доступ к приватным полям), который может все провалидировать и выдать или ошибки или твой объект.

В треде я точно мельком видел билдеры, но не видел, чем это не устроило.

qnikst ★★★★★
()
Последнее исправление: qnikst (всего исправлений: 1)
Ответ на: комментарий от anonymous

жава головного мозга
на нормальном языке без костылей и говнопаттернов

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

как этот хелловорд пишется на нормальном языке без костылей и говнопаттернов

и сами не поняли что сказали.

Проблема в том, что ваш код абсолютно идентичен любому коду из ваших примеров с вики. Ничего не изменилось. Ничего. Вы даже этого не видите. Все у вас круче, ну потому-что метапрограммирование, ну потому-что only object - only-Ъ, меньше строчек - всех в [censored] и т.д. и т.п. Ну дак никто не спорит, что Io имеет свою идею, и свою прелесть, но каждый язык хорош, да в меру.

  • Вы не решили проблемы, о которых сами писали выше. Наслаждайтесь дальше этим мёдом, слава богу это законно=)
  • Вы не привели аргументы в пользу вашего кода и не привели конкретных профитов от него, а посему я делаю вывод что вы просто поклоняетесь своим в слотик сообщение/в слотик сообщение/etc, сами не понимая его профита

Мне открыли глаза, я просто недавно зарегистрировался здесь, а вы этим воспользовались :( Как это низко с вашей стороны, анонимус :(

Ты ведь не в курсе, кто такой анонiмус, да?

Теперь в курсе, больше не поведусь :)

znenyegvkby
()
Ответ на: комментарий от znenyegvkby

отнюдь, не преуменьшая полезности других постов, этот был весьма познавательным: а именно, вам удалось внятно показать границы применения сего паттерна

anonymous
()
Ответ на: комментарий от winlook38

Лучше давать примеры, когда формируемый Строителем объект иммутабелен, так понятнее и логичнее, ведь Строитель формирует представление каких-то данных. Иначе профит перед голой иерархией классов действительно трудно увидеть

Разве? А если не отрываясь от примера, приведённого выше, Строитель может строить младенцев, стариков, или 40-летних людей. Однако, каждый из уже построенных объектов может естественным образом инкрементировать возраст. В этом случае, представление данных выходит мутабельным и это будет удобно.

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

anonymous
()
Ответ на: комментарий от monk

судя по vector<double> это должен быть С++, судя по отсутствии точки запятой после определения класса и отсутствию модификаторов доступа, это какой-то неизвестный мне язык.

в плюсах вы можете использовать boost::optional, для вашего языка поищите аналог

anonymous
()
Ответ на: комментарий от monk

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

blexey ★★★★★
()
Ответ на: комментарий от anonymous

Строитель может строить младенцев, стариков, или 40-летних людей
Однако, каждый из уже построенных объектов может естественным образом инкрементировать возраст

Придется объяснять зачем вообще разные классы для младенцев, стариков и 40-летних людей, если это просто вычисляемое свойство.

winlook38 ★★
()
Ответ на: комментарий от anonymous

в плюсах вы можете использовать boost::optional

Там внутри также дополнительное поле... То есть для сложной структуры, предлагаешь поля делать с NULL. Как в SQL. Ладно, тоже вариант, хотя, на мой взгляд, громоздкий. Потом на каждое чтение проверку придётся делать.

monk ★★★★★
() автор топика
Ответ на: комментарий от winlook38

зачем вообще разные классы для младенцев, стариков и 40-летних людей

Например, у них разные методы. Кто-то писал, что в ООП конструкция

switch(type)
{
   TYPE1:    
   ....
   TYPE2:
   ....
   TYPE3:
   ....
}
является признаком того, что что-то явно сделано не так.

monk ★★★★★
() автор топика
Ответ на: комментарий от blexey

Да как угодно, лишь бы не обозначать отсутствие значения значением из самого их множества.

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

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

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

winlook38 ★★
()

правило простое: ничего не делать впрок

anonymous
()

А если лень дублировать описание 100500 полей. А нужны объекты с разным поведением (существовать с ошибками и без), то можно и через наследование:

#include <iostream>
#include <memory>


class MyItemData {
public:
  int f1;
  int f2;
  double f3;
};

class MyItem: public MyItemData {
private:
  MyItem();
public:
  static bool check(std::shared_ptr<MyItemData> data) {
    return (data->f2==3);
  };
  static std::shared_ptr<MyItem> build(std::shared_ptr<MyItemData> data) {
    if (check(data)) {
      return std::make_shared<MyItem>(static_cast<const MyItem&>(*data));
    } else {
      return std::shared_ptr<MyItem>();
    }
  };
};


int main() {
  auto data = std::make_shared<MyItemData>();
  data->f1 = 1;
  data->f2 = 3;
  data->f3 = 2.3;
  
  auto tmp1 = MyItem::build(data);
  if (tmp1) {
    std::cout << tmp1->f2 << std::endl;
  } else {
    std::cout << "NULL\n";
  }

  data->f2=4;
  auto tmp2 = MyItem::build(data);
  if (tmp2) {
    std::cout << tmp2->f2 << std::endl;
  } else {
    std::cout << "NULL\n";
  }

  return 0;
}
AlexVR ★★★★★
()
Ответ на: комментарий от monk

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

Изначально вообще не шло речи о каком-либо 20-угольнике. Эт ты сам привёл его в качестве примера и выдумал некорректные ограничения:

При создании объекта он инициализируется нулями (так как больше нечем) и объект становится изначально невалидным.

Накопление вершин соответствующим методом подразумевает существование ситуации, когда необходимые проверки по нескольким вершинам нет смысла выполнять. Для этого проверка должна быть снабжена соотвествующим условием. Что тут непонятного?

blexey ★★★★★
()
Ответ на: комментарий от blexey

Изначально вообще не шло речи о каком-либо 20-угольнике.

Изначально: объект с достаточно большим количеством полей. Причём, для объекта они важны (почти) все, значений по-умолчанию у них нет и они зависят друг от друга.

Накопление вершин соответствующим методом ... Для этого проверка должна быть снабжена соотвествующим условием.

Проще сделать у объекта целиком функцию init(), которая будет ставить флаг «объект завершён» и проверять корректность объекта. Это эффективней и проще, чем на каждое поле добавлять значение «не определено».

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

Ну ок, наверно меня сбил с толку не совсем корректный пример с 20-угольником, вершины которого я посчитал более корректным хранить в динамической структуре, а не в отдельных полях.

Проще сделать у объекта целиком функцию init(), которая будет ставить флаг «объект завершён» и проверять корректность объекта. Это эффективней и проще, чем на каждое поле добавлять значение «не определено».

Согласен.

blexey ★★★★★
()
Ответ на: Про смысл слов от DonkeyHot

Нет, в данном конкретном контексте сложный объект воспринимается именно как сложный для понимания за счет разных представлений. А

«сложен из чего бы то ни было».

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

znenyegvkby
()
Ответ на: Про смысл слов от DonkeyHot

Причем фраза

«сложный» это, всё таки, не «трудно понять», а «сложен из чего бы то ни было».

отлично описывает состояние конечно продукта собранного на базе строителя. Но я, в комментарии, говорил о сложности объекта - для которого имеет смысл применять/не применять стоителя.

znenyegvkby
()
Ответ на: комментарий от monk

является признаком того, что что-то явно сделано не так

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

no-such-file ★★★★★
()
Ответ на: комментарий от znenyegvkby

в данном конкретном контексте сложный объект

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

DonkeyHot ★★★★★
()
Ответ на: комментарий от DonkeyHot

Согласен с

Сложное достаточно часто бывает непонятным, трудным для чего-либо, и т.п. - но это не синонимы.

но ведь мне уже отвечали на тот комментарий, и мы общались с другими участниками там же, ниже. Никакой путаницы в понятиях не было, или я не вижу ситуацию так, как ее видите вы? Тогда предложите альтернативу

«сложный» это, всё таки, не «трудно понять», а «сложен из чего бы то ни было».

Как стоило выразиться, чтобы недопонимания не возникло?

znenyegvkby
()
Ответ на: комментарий от znenyegvkby

Никакой путаницы в понятиях не было

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

DonkeyHot ★★★★★
()
Ответ на: комментарий от DonkeyHot

Спасибо, учту на будущее. Постараюсь быть внимательнее к понятиям.

znenyegvkby
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.