Двойно изпращане
Още няколко резерви, преди да формулирам задачата по-стриктно: няма да се спирам на това защо динамичната, изрична проверка на типа чрез кастинг и отражение не е подходяща за мен. Има две причини за това: 1) целта е да се отървем от изключенията по време на изпълнение 2) Искам да покажа, че езикът е доста изразителен, дори ако не прибягвате до изброените средства и оставате в рамките на силното писане.
С други думи, нека приемем, че имаме определена колекция от разнородни обекти с доста общ и рядък интерфейс. Вземайки произволни две от тях, искаме да разберем вида на всеки и да го обработим със специален метод.
Пример:
- да кажем, че има някакъв интерфейсICell, който е имплементиран от класоветеRedCell,BlueCell,GreenCell.
- има правила като това:
- след като получихме две червени клетки, пишем "червено на червено"
- след като получихме зелено и синьо, пишем "брега"
- във всички останали случаи посочете първия и втория цвят
- чрез директно произведение на набора от клетки по себе си, получаваме:
червено върху червено червено --> зелено червено --> синьо зелено --> червено зелено --> зелен брег син --> червено синьо --> зелено синьо --> синьо
Мисля, че е очевидно, че за да решим такъв проблем, един клас от формуляра би ни бил достатъчен:
Разбира се, на практика трябва да имате достъп до специфични свойства на обекти, които не могат да бъдат поставени в общия интерфейс, но претрупването на примера със сложна област на домейн не е удобно, така че нека се съгласим: няма нито дума за цвят в общия интерфейс и всички клетки изглеждат като тази червена по-долу:
Класически посетител
Решението на такъв проблем с единелемент. Няма да се спирам на това, можете да научите повече за това, например, в Wikipedia. В нашия случай решението ще изглежда така. С такъв модел (нека обозначим този код като [1], ще го запомня по-долу):
Прост посетител лесно решава нашия проблем:
Прилагане на посетител към задача
Е, нека се опитаме да обобщим съществуващото решение за случая на два елемента. Първото нещо, което идва на ум, е да превърнем всяка клетка в посетител: тя вече знае своя тип и след като посети приятеля си, ще разпознае неговия тип, следователно ще реши нашия проблем. Оказва се нещо подобно (за простота ще напиша решение само за червената клетка, в противен случай вече има много код):
Сега всичко, което трябва да направим, е да добавим прост процесор и проблемът е решен!
Критика на намереното решение
Това изобщо не е добро! Не мога да създам клетка, без първо да реша какъв тип ще се върне посетителят! Пълни глупости.
Разбира се, можете да се отървете от обобщението: оставете посетителя да не връща нищо, но променете състоянието му, което ще го изложи по открит начин, но: (1) Предпочитам неизменно състояние и програмиране, предубедено към функционално (нека пропусна мотивацията - мисля, че е доста ясно), така че трябва да избягвате действия (Действие) и да се стремите да използвате функции (Забавление), така че би било добре посетителят да върне тип. Надявам се, че горното ще бъде достатъчно, за да потвърди мнението ми - решението е така, но има още една важна забележка, на която искам да се спра по-подробно. Нека помислим колко метода трябва да съдържаIProcessor?N x N. Това е много. Но в крайна сметка най-вероятно се нуждаем от специална обработка за много малък, линеен софтуер.N, брой случаи. И все пак не можем да знаем предварително кой от тях ще ни бъде полезен (а ние пишем рамка, нали? Класова структура, база, която всеки ще използва по-късно, когато свързва нашето събрание с техните решения).
Как може да се подобри? Очевидната стъпка е да отделите модела от посетителя. Да, нека, както преди, всяка клетка може даAcceptVisitor(. ), но всички методи на Visit ще бъдат в отделни класове. Лесно е да разберем от какво се нуждаем, в този случайN+1клас, всеки от които съдържаNПосететеметоди. Не е слаб, нали? В този случай всяка нова клетка води до добавяне на нов клас + метод към всяка от съществуващите.
Намерено е най-доброто решение
Имам решение, което няма тези недостатъци, а именно: трябват ми няколко класа (казвам без точност, защото към това число се добавят разни синтактични прелести като плавен интерфейс, на които не можах да устоя, но дали да ги използвам е въпрос на вкус), като броят на класовете зависи отN; когато добавям нова клетка, ще трябва да добавяNнезависим брой методи към тези класове.
Ако е така, супер, пиши ми, но ето моята:
Нашият модел все още е във формата [1], но ето как (за да заинтригуваме малко търпеливия читател) аналогът на конкретен процесор от предишния пример ще изглежда така:
където ColorRetriever е обикновен „единичен“ посетител:
[Преди, всъщност, самото решение, малко отклонение относно този последенColorRetriever- искам да насоча вниманието на читателя към факта, че самите редове
все още не са решили проблема. С тяхна помощ ние просто осъществяваме последователен достъп до всяка от двете клетки под формата на явен тип, а не скритинтерфейс, докато трябва да получим такъв достъп и за двете едновременно. Поправката е направена благодарение на вниманието на maxim_0_o, моите поклони пред него]
Както можете да видите, ние определяме общия случай и два частни и това ще бъде достатъчно, за да конструираме решение. Като цяло ще ни трябват два класа - първият и вторият посетител. Вторият ще бъде общ, а първият ще създаде негов въведен екземпляр, който да се използва за конкретна клетка.
И още един интерфейс за красота, за отделяне на строителя от посетителя (които се сливат в двата класа, но синтаксисът на повикването е красив):
В заключение бих искал да се позова на поредица от статии за "магьосници и воини", където също се обсъждат проблемите с изпращането в C#.
И тук можете да получите грант за тестов период на Yandex.Cloud. Необходимо е само да въведете "Habr" в полето "секретна парола".