Дайте малко feedback за глава 23 - Мементо

26 views
Skip to first unread message

Borislav Ivanov

unread,
Apr 9, 2013, 1:35:04 PM4/9/13
to design-pat...@googlegroups.com
Здравейте писатели, 
Последните итерации пиша по глава 23 - Мементо. Ники ми направи едно ревю, при което възникнаха два въпроса. Тъй като възможните решения си имат плюсове и минуси, решихме да се допитаме до мъдростта на народа :) А проблемите са следните:
  1. Твърде специфична имплементация. Всъщност идеята я взех от wikibooks - интересното при нея е, че методите за създаване/модификация на мементото са недостъпни за други класове освен за създателя (originator-а), като по този начин то остава истински капсулирано. Проблемът е, че е доста специфична за Java (използва се не толкова известния факт, че ако клас B е вложен в клас A, то private членове на класа B са достъпни за А). Аз се опитах да избегна този проблем, като посочих още 2 (също толкова красиви и също толкова специфични :)) имплементации за C# и C++. От друга страна обаче, нали се опитваме да направим книгата платформено-независима? Кое според вас е правилното решение - да жертваме ли красивите имплементациите и да ги заменим с обща такава (която би могла да се имплементира в повече езици), или все пак да ги оставим?
  2. Прилагане на мементото за възстановяване на състоянието на друг обект - т.е. състоянието се извлича от обекта A, а се възстановява в по-късен момент на обекта B (това се получи, тъй като се опитвах да измисля максимално прост пример). Според вас грешно ли е това? Наистина, в класическата имплементация състоянието се запаметява и възстановява за един и същи обект, но според мен това не е задължително - поне в случая когато в мементото се съхранява цялата информация, необходима за довеждането на обекта до дадено състояние. В източниците дори се посочва като пример за приложение на Мементо правенето на Save в компютърните игри, което си е точно същото - там цялата система би могла да се стартира от нулата и да се доведе до дадено състояние благодарение на информацията записана в Save-файла от предишно стартиране на играта.
Поздрави, 
Боби

Николай Василев

unread,
Apr 10, 2013, 5:14:59 PM4/10/13
to design-pat...@googlegroups.com
Здравейте,

Извинявам се за забавянето. 

Ако някой е ползвал мементо или му е интересно да го погледне, ще може ли да хвърли едно око на главата на Боби?

Относно въпросите:

  1. Реализацията е брилянтна и е най-оптималната, когато се използва езика Java. Предполагам и за C# ще е нещо подобно.

    Въпросът е, че фактът, че мементото трябва да имплементира два интерефейса - един за потребителя и един за този, който го съхранява, остава скрит заради особеностите на вътрешните (nested) класове в Java-та, взаимодействието им с обвиващият ги клас, както и класовете извън него.
    Ако се направи имплементация, при която мементото реализира два отделни интрфейса - един за обекта от който се взима мементо и което знае вътрешностите на мементото и друг - за този, който ще го съхранява, ще се наруши недостъпостта на вътрешностите му.

    Според вас, в случаи като този, когато трябва да се изложи някаква концепция/шаблон, макар и по неефективен, но по-ясен, начин трябва ли да го правим?

    Аз обикновено предпочитам да го изложим dummy вариант, жертвайки ефективността (в случая мементото да наследи два нормални джавешки интерфейса), но да илюстрираме още една опция на читателя. След това естествено трябва да добавим забележка, че тази имплементация има сериозни недостатъци и който го прави така, го прави на собствен риск.

  2. Това е чисто имплементационен детайл и предполагам зависи от конкретния случай как ще се направи - при възстановяване на състоянието на мементото - дали да се създаде нов обект и състоянието му да се нагласи, вземайки стойности от мементото или за целта ще се преизползва стария обект. 

    Отново въпросът в групата е по-скоро свързан с изложението на материята отколкото с имплементацията като такава. Ако погледнете примера, създаването на нов обект според вас би ли объркало читателя или не (в случая вас, ако гледате на материала като читател неопитен  в шаблоните, но със задоволително ниво на програмирането като цяло)? 
Поздрави,
Николай




--
Получихте това съобщение, защото сте абонирани за групата „Design Patterns Book“ в Google Групи.
За да се отпишете от тази група и да спрете да получавате имейли от нея, изпратете имейл до design-patterns-...@googlegroups.com.
За да публикувате в тази група, изпратете имейл до design-pat...@googlegroups.com.
Посетете тази група на адрес http://groups.google.com/group/design-patterns-book?hl=bg.
За повече опции посетете https://groups.google.com/groups/opt_out.
 
 



--
Николай Василев

Mihail Stoynov

unread,
Apr 11, 2013, 8:18:32 AM4/11/13
to design-pat...@googlegroups.com
Зачетох се в главата и следните неща ми направиха впечатление:
неправилна променлива:

1) public void stop() {
            long endTimeInNanoseconds = System.nanoTime();
            this.milliseconds +=(endTimeInNs - startTimeInNs) / 1000000;
      }

2) ники е писал тоя 100000 да се кръсти nanosecondsInSeconds, а трябва да е nanosecondsInMilliseconds.




А сега по въпросите
1. Имплементацията със статичен инър клас е яка. Ако трябва да има втори интерфейс, той къде ще се сложи? Ще са два ненаследяващи се интерфейса (един за съхранителя на състоянието и един за Оригинатора), или единия ще наследява другия?
Ако са ненаследяващи са, тоест равнопоставени, то тогава Съхранителят не би трябвало да знае за втория (за вътрешна употреба) интерфейс. И така или иначе ще има instanceof и кастване вътрешно (тоест реално вторият интерфейс не се ползва за ООП абстракция). Ако са наследяващи са, то зависи кой е по-надолу в йерархията, в единия случай Съхранителя ще има достъп до тези методи, в другия пак има instanceof (при подаване на състоянието на нов Оригинатор (мементо) обект).
Ники, ти казваш, че ако има втори интерфейс, то Съхранителя може да направи instanceof и да пипа вътре. Поне аз така разбирам думите ти. Ама Съхранителя ако иска да пипа вътре, нищо не може да го спре. Спринг например като си прави проксита и им слага допълнителни интерфейси, но те не нарушават абстракцията, защото _не би трябвало_ някой да ги пипа.
Тоест според мен втория интерфейс би бил нужен само откъм по-ясно открояване на това кой каква роля има и какво може да пипа.
Кратка първа имплементация с два интерфейса и instanceof не пречи да се сложи първо, но само за яснота. Аз се сещам само за instanceof при смяна на интерфейсите, през които се работи с даден клас. Има ли друг по-елегантен начин? Може с package-local, package-private, friendly полета в отделен пакет? Но пак няма да е много чисто. Това с вътрешния клас е най-добро. За жалост се работи с клас, а не с интерфейс.

2.Да може да се restore-ва различен обект от създателя на сеттингс, според мен, е важно да може да се прави. Имплементационно разликата е малка, но е по-мощно. Говоря за коментар 21, 22 в главата. А там вместо нова инстанция може да се ползва сингълтън, но тогава реално няма нужда от мементо. Та освен нов обект, не виждам как иначе може да се процедира.

:)
Михаил





2013/4/11 Николай Василев <nikolay...@gmail.com>

Николай Василев

unread,
Apr 14, 2013, 12:35:43 PM4/14/13
to design-pat...@googlegroups.com
Здравейте,

Извинявам се забавянето на отоговора отново.

@Мишо: относно първите две забележки, да, предполагам, че Боби ще приложи промените. Главата все още е в процес на работа и се изчистват последните детайли преди да влезе в редакция. Мерси за бележката все пак :)

Относно първия въпрос:
  • Целта на този въпрос беше по-скоро генерална - отнасяща се за цялата книга, т.е. ако дадена имплементация е неефективна по един или друг начин, но разкрива някаква характеристика на шаблона, трябва ли да я включваме в книгата?

    Аз съм на мнение, че е хубаво, като също така, трябва да се отбележи ясно, че има недостатъци (в повечето случаи заради ограничението на Java-та) и ако читателят избере тази иплментация - то е на собствен риск

  • може би не съм се изразил много добре по отношение на съхранителя. Съхранителят не би трябвало да може да пипа и да знае какво има вътре в мементото. Мисля, че някъде трябваше да е отбелязано това в главата. Така че на него му трябва просто един маркер интерфейс (без методи), за да приеме мементото и да го съхрани (ако допуснем, че имплементацията се прави с джавешки интерфейси).

  • да, както ти каза, може да се направи по два начина - с два независими интерфейса (http://stuff.nvasilev.com/pics/jing/memento-two-independent-interfaces.png) и с наследяващи се интерфейси (http://stuff.nvasilev.com/pics/jing/memento-two-interfaces-extension.png) .
    • Има и трети вариант - може да се мине и без narrow интерфейса, като съхранителят приема Object за съхранение и когато му се поиска мементо - връща Object. 

  • всички случаи не са "чисти" както ти каза, откъм архитектурна/имплементационна гледна точка (ако говорим за production code), точно заради капсулацията на данните и reflection-а.
    От друга страна и при текущата имплементация би трябвало също да можеш да разпитваш вътрешните класове? Мисля, че Java-та ти позволяваше да го правиш. 

  • Когато създателя (originator) взема мементото, за да върне предишно състояние, проверката с instanceof е неизбежна според мен и в двата случая - на разширяващи се и неразширяващи се широк/тесен интерфейс. 

  • "Tеоретично" съхранителят не би трябвало да знае какъв обект точно е съхранен в него, но на мен идеята ми беше по-скоро да се направи такъв пример, за да се подчертае, че има два интерфейса - един за съхранителя (който не предоставя никаква информация какво представлява мементото) и другия за създателя - който пипа състоянието съхранено в мементото чрез предоставените му методи от широкия интерфейс.

    Накрая, след ако се представи така имплементацията, се слага една от онези таблици с удивителния знак, за привличане на вниманието и се казва, че тоя пример е по-скоро с образователна цел - да се разграничат двата интерфейса, през които мементото се "гледа" от различните участници в шаблона и употребата на такава имплементация в prod code е на собствен риск. За да се подсили казаното, се изброяват недостатъците, които ти спомена и които произлизат от синтаксиса на Java. 
Относно втория въпрос:
  • това, което ме притеснява е, че ако създателят (TimerApplication) има identity, то може да се загуби. Ако ти е persistance обект, който е сериализиран с базата да речем, използвайки new, реално ти му даваш ново identity. 

  • Иначе нищо лошо не виждам да се създаде нов създател и да му се set-не вече създадено мементо, но Боби е прав в случая, че това може да направи примера по-тежък.

  • За мен в момента е ок примера, просто това със загубата на "идентичността" (как се предполага, че се превежда "identity" да му се невиди :) ) ме смути малко.
    Може би, за да запазим примера опростен, можем да го оставим така и или като бележка под линия, или като отделна подсекция на примера, да обясним, че когато идентичността на TimerApplication е от значение, трябва да се избегне създаването на нов обект, а създателя само да копира стойностите от полетата на мементото в неговите си полета.
Поздрави,
Николай

Mihail Stoynov

unread,
Apr 15, 2013, 6:32:27 AM4/15/13
to design-pat...@googlegroups.com
Описват се всички варианти, няма друг начин :)


2013/4/14 Николай Василев <nikolay...@gmail.com>
Reply all
Reply to author
Forward
0 new messages