Loading...

Във форума е въведено ограничение, което позволява на потребителите единствено да разглеждат публикуваните въпроси.

ArmenPotourlyan+deleted! avatar ArmenPotourlyan+deleted! 488 Точки

[Exercises][C# OOP Advanced] - Reflection - Problem{6} - Mirror Image

Здравейте, колеги,



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

private Wizard leftWizardReflection;
private Wizard rightWizardReflection;

 

При такава структура за изпълнението на заклинанията е достатъчно да се обходи магьосника с рекурсивен Depth-First Search Pre-order алгоритъм. При този алгоритъм рекурсивно се обхожда първо едната част, после другата част на дървото (от някой стартов възел - в случая магьосник). Ето как изглеждат заклинанията:

public void CastReflectionSpell()
{
    this.ReflectionSpellCasted?.Invoke(this, EventArgs.Empty);
    this.CastReflectionSpell(ref this.leftWizardReflection);
    this.CastReflectionSpell(ref this.rightWizardReflection);
}

public void CastFireballSpell()
{
    this.FireballSpellCasted?.Invoke(this, EventArgs.Empty);   
    this.CastFireballSpell(ref this.leftWizardReflection);
    this.CastFireballSpell(ref this.rightWizardReflection);
}

ReflectionSpellCasted и FireballSpellCasted са еvent-и, към които можем да се закачим отвън :))) И съответно при всяко извикване от оригиналния магьосник или от някое от копията му - се вдигат event-ите последователно първо по лявото разклонение, после по дясното, така, както се иска в условието на задачата.

Как да избираме обаче точно определено копие на магьосника като начален възел? Тук определено ми куца структурата - аз съм решил проблема със статичен речник <int, Wizard>, който държи срещу id съответния Wizard. Направил съм индексатор, който да връща стойностите от речника:

public Wizard this[int index] => wizardById[index];

Какъв е недостатъка с този подход? Няма да мога да направя друг оригинален магьосник, понеже статичният речник е споделен за всички инстанции на Wizard... Разбира се, специално за тази задача няма значение - имаме нужда само от един оригинален магьосник. Все пак дали можете да предложите идея как да направя достъпа до даден възел (копие на маг), така че да мога да инстанцирам безопасно колкото си искам нови оригинални магьосници?

Ето как изглежда целия клас: Wizard

Тагове:
1
C# OOP Advanced 28/07/2016 11:58:06
stambi4a avatar stambi4a 126 Точки
Best Answer

    Не мога да видя как точно ти работи проекта, но защо не направиш речника да e Dictionary<string, Dictionary<int, Wizard>>. Ще загубиш индексатора, но няма как. Освен това ще трябва да го изкараш речника от класа Wizard.  При това положение и логиката на екстрактване на командите ще се промени, понеже ще трябва по всяко едно време да можеш да създадеш wizard, не само от първият ред, а и параметрите ще са други. Като цяло промените не са много когато работиш с повече от 1 оригинал.

    Според мен статичната база е лоша практика, аз използвам  Repository Pattern. В единственият евент слагам List<int> Ids, кoйто се екстрактва от извикващият метод и за всеки елемент се прилага клониране. Извиквашият метод има параметър repository-то, не се намира в wizard-a. Ако твоята база е отделна, няма да ти трябва Dfs, просто при създаването на нов clone на wizard-а ще закачаш евентите към неговите CastReflectionSpell и CastFireballSpell.

    Ето моето решение:  Цък  Липсват няколко файла, но те са без значение за цялостната концепция.

    Идеалният случай беше командата за магия да създава магия която да се изпълнява вътре в wizard-a, но съкратих с една стъпка и няколко класа.Освен това не трябва wizard-а да знае за базата,  следователно магията трябва да има евент който се връзва с клас който има връзка с базата - например commandExecutor-а. Това би бил най-ООП варианта който се сещам.

 

0
28/07/2016 21:47:27
ArmenPotourlyan+deleted! avatar ArmenPotourlyan+deleted! 488 Точки

Благодаря много за отговора и решението. Определено ми даде храна за размисъл. Ако решиш следващата задача 1984, бих те помолил да споделиш решението си - моето стана много грозно, пък и виждам, че има поука от твоя код :)))

1
29/07/2016 08:19:49
stambi4a avatar stambi4a 126 Точки

    Ами и моето решение е грозно, спестих доста код  - всякакви фактори-та и 100 реда клас engine, защото направих евента generic, но това доведе до проблем с хоокването на евента и забавяне заради по - сложният reflection. Така, че и ООП-то е кофти и има много излишни неща.

   Според мен, се решава с имплементиране на IsPropertyChanged interface от .net, макар да не съм сигурен дали с него може да се избегне хуукването за всяка инстанция на клас или директно се хууква като статичeн евент. Пускането на евентите ще е далеч по-лесно ако не са generic.

   Като цяло хинтовете са много оскъдни, трябва бая research, използва се много reflection почти навсякъде, макар, че с анотации трябва да става по-бързо.

   Ще се радвам, ако се даде по-нататък едно оптимизирано решение.

   Поздрави.

1
kaloyannikov avatar kaloyannikov 531 Точки

Междудругото Fireball не трябва ли да се каства само от 1 wizard т.е без и разклоненията му да кастват?

0
ArmenPotourlyan+deleted! avatar ArmenPotourlyan+deleted! 488 Точки

Всички копия под даден магьосник правят Fireball:

After that, if wizard 0 casts Fireball, the sequence in which his mirror images will cast fireball after him would be 1, 3, 4 and then 2, 5 and lastly 6.

1
28/07/2016 11:56:33
kaloyannikov avatar kaloyannikov 531 Точки

да прав си ,извинявай за неадекватния коментар :D Иначе аз също го направих рекурсивно обхожда всеки подWizard и му каства определената магия но ползвам List<Listener> и за всеки вика update() като по тоя начин ако някой друг има Listener ще викне и неговия update() . 

1
28/07/2016 12:02:32
alexei.tcekov avatar alexei.tcekov 33 Точки

Здравейте колеги ... и аз да споделя едно решение с Вас ... след цял ден в грешна посока и опити да преправям се отказах и започнах отначало ... решението ми е с няколко евента  и горе долу всеки си знае задачата ... main-а го оставих така че исках да проверя в джъджа какво съм измислил ... https://github.com/alexeitcekov/school/tree/master/C%23/OOPAdvance/homework/ReflectionAndAttributes/MirrorImageV2

1
StaVykoV avatar StaVykoV 169 Точки

DFS ми беше първата мисъл като видях тази задача. По принцип си мисля, че може да се реши без репозитори и евенти, но няма много смисъл.


Решение без репозитори:
Магията и айдито на кастъра се пускат винаги на първия магьосник, те после се предават надолу по дървото, докато не сен амери магьосника с айдито. Когато се открие, от него започва самото изпълнение на магията надолу.

Недостатъка е, че може да стане така, че да се обхождат доста магьосника докато се намери правилния, докато от репозиторито има достъп веднага. Плюс не виждам голям, освен малко спестена памет.

 

Решение с репозитори:

Просто частно репозитори което слуша за създаден нов магьосник. Така магьосниците няма да знаят за самото репозитори.

ID- то може да се генерира от нициализиран IDGenerator който си се предва по магьосниците от едно дърво. Той ще е реално просто еидн клас който прави ++ на int и го връща. Или просто може да се бутат в лист и от там автоматично да се генерира Id

Eто го моето решение - ЦЪК
Мисля си, че има по - добър вариант като например магиите да се изкарат във фактори, но в 5 сутринта малко ми е писнало и го оставям така :D

1
06/08/2016 05:24:16
Можем ли да използваме бисквитки?
Ние използваме бисквитки и подобни технологии, за да предоставим нашите услуги. Можете да се съгласите с всички или част от тях.
Назад
Функционални
Използваме бисквитки и подобни технологии, за да предоставим нашите услуги. Използваме „сесийни“ бисквитки, за да Ви идентифицираме временно. Те се пазят само по време на активната употреба на услугите ни. След излизане от приложението, затваряне на браузъра или мобилното устройство, данните се трият. Използваме бисквитки, за да предоставим опцията „Запомни Ме“, която Ви позволява да използвате нашите услуги без да предоставяте потребителско име и парола. Допълнително е възможно да използваме бисквитки за да съхраняваме различни малки настройки, като избор на езика, позиции на менюта и персонализирано съдържание. Използваме бисквитки и за измерване на маркетинговите ни усилия.
Рекламни
Използваме бисквитки, за да измерваме маркетинг ефективността ни, броене на посещения, както и за проследяването дали дадено електронно писмо е било отворено.