Посетител (шаблон)

UML диаграма на шаблона Посетител

В обектно-ориентираното програмиране и софтуерното инженерство, дизайнерският шаблон, наречен „посетител“, представлява метод за отделяне на даден алгоритъм от обектната структура, върху която оперира. Практическата полза от подобно отделяне идва от възможността да се добавят нови свойства и операции към съществуващи вече структури без да се налага модификация. Заради свойствата си посетителският модел е един от начините да се следва отворено/затворения принцип.

Visitor in LePUS3 (legend)

По същество, посетителят дава възможност на потребител да добавя виртуални функции на семейство класове без да ги променя. За целта се създава посетителски клас, който имплементира всички, подходящи специализации на виртуалната функция. Посетителят приема за входящи данни референция на класа и постига желания резултат, чрез така нареченото двойно изпращане.

Дефиниция

Авторите на „Design Patterns – Шаблони за дизайн: Елементи на обектно-ориентирания софтуер за многократно използване“ определят Посетителят като: „Операция, която да бъде изпълнена върху елементи на обектна структура. Посетителят дава възможност за дефинирането на нови операции без да се модифицират класовете от елементи върху които оперира.

В своята същност Посетителят е идеалният шаблон за достъп до публични библиотеки, защото позволява изпълнението на операции върху класове чрез „посетителски“ клас, което от своя страна спестява преработката на оригиналния код.

Мотивация

Да вземем за пример разработването на 2D CAD система. В своето ядро има няколко типа, които представляват основните геометрични форми като кръгове, линии и дъги. Субектите са подредени на слоеве, като на върха на йерархията стои типа рисунка, който е просто списък на слоеве, плюс някои допълнителни свойства.

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

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

Вместо това, може да се прилага схемата Посетител. Посетителският шаблон кодира логическата операция на цялата йерархия в един клас, съдържащ един метод за всеки тип. В примера за системата CAD, всяка записваща функция ще бъде изпълнена като отделен Посетителски подклас. Това ще премахне дублирането на всички проверки за тип и ще накара компилаторът да се оплаче, ако формата е пропусната.

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

Детайли

Моделът Посетител изисква език за програмиране, който поддържа единично изпращане. При това условие, вземаме за пример два обекта, всеки от някакъв вид клас; единият се нарича „елемент“, а другият се нарича „посетител“. Елемент има метод accept(), който може да приема посетител като аргумент. Метода accept() извиква метода visit() на посетителя; елемент се изпраща като аргумент на метода на visit(). По този начин:

  • Когато accept() метода бъде извикан от програмата, неговата имплементация се избира въз основа на следните две условия:
    • Динамичният тип на елемента.
    • Статичният вид на посетителя.
  • Когато асоциираният visit() метод бъде извикан, неговото изпълнение се избира въз основа на следните две условия:
    • Динамичният вид на посетителя.
    • Статичният тип на елемента, който е в рамките на имплементацията на метода accept(), и който е еквивалентен на динамичния тип на елемента. (Като бонус, ако посетителят не може да се справи с аргумент от дадения тип елемент, тогава компилаторът ще хване грешката.)
  • Следователно, имплементацията на метода на accept() се избира въз основа на следните две условия:
    • Динамичният тип на елемента.
    • Динамичният вид на посетителя.

Това на практика изпълнява двойно изпращане и тъй като Common Lisp езика поддържа многократно изпращане (не само единично изпращане), прилагането на шаблона посетител в Lisp е тривиално.

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

Java пример

Следният пример е от Java, и показва как съдържанието на дърво от възли (във този случай описващо компонентите на автомобил) може да бъде отпечатано. Вместо да създаваме „отпечатващ“ метод във всеки от подкласовете (Колело, Двигател, Тяло, и Автомобил), създаваме само един посетителски клас CarElementPrintVisitor, който да изпълнява отпечатването. Тъй като различните подкласове имат различни изисквания за отпечатването, CarElementPrintVisitor дефинира отпечатващи visit() методи с различен тип на аргумента. CarElementDoVisitor извършва други действия с компонентите на автомобила и дефинира различните visit() методи по подобен начин. CarElementPrintVisitor и CarElementDoVisitor се отнасят така, както се отнасят посетителите, които запазват графичните данни в различен файлов формат.

Диаграма

Източници

interface ICarElementVisitor {
 void visit(Wheel wheel);
 void visit(Engine engine);
 void visit(Body body);
 void visit(Car car);
}

interface ICarElement {
 void accept(ICarElementVisitor visitor); // CarElements have to provide accept().
}

class Wheel implements ICarElement {
 private String name;

 public Wheel(String name) {
 this.name = name;
 }

 public String getName() {
 return this.name;
 }

 public void accept(ICarElementVisitor visitor) {
 /*
 * accept(ICarElementVisitor) in Wheel implements
 * accept(ICarElementVisitor) in ICarElement, so the call
 * to accept is bound at run time. This can be considered
 * the first dispatch. However, the decision to call
 * visit(Wheel) (as opposed to visit(Engine) etc.) can be
 * made during compile time since 'this' is known at compile
 * time to be a Wheel. Moreover, each implementation of
 * ICarElementVisitor implements the visit(Wheel), which is
 * another decision that is made at run time. This can be
 * considered the second dispatch.
 */
 visitor.visit(this);
 }
}

class Engine implements ICarElement {
 public void accept(ICarElementVisitor visitor) {
 visitor.visit(this);
 }
}

class Body implements ICarElement {
 public void accept(ICarElementVisitor visitor) {
 visitor.visit(this);
 }
}

class Car implements ICarElement {
 ICarElement[] elements;

 public Car() {
 //create new Array of elements
 this.elements = new ICarElement[] { new Wheel("front left"),
 new Wheel("front right"), new Wheel("back left"),
 new Wheel("back right"), new Body(), new Engine() };
 }

 public void accept(ICarElementVisitor visitor) {
 for(ICarElement elem : elements) {
 elem.accept(visitor);
 }
 visitor.visit(this);
 }
}

class CarElementPrintVisitor implements ICarElementVisitor {
 public void visit(Wheel wheel) {
 System.out.println("Visiting " + wheel.getName() + " wheel");
 }

 public void visit(Engine engine) {
 System.out.println("Visiting engine");
 }

 public void visit(Body body) {
 System.out.println("Visiting body");
 }

 public void visit(Car car) {
 System.out.println("Visiting car");
 }
}

class CarElementDoVisitor implements ICarElementVisitor {
 public void visit(Wheel wheel) {
 System.out.println("Kicking my " + wheel.getName() + " wheel");
 }

 public void visit(Engine engine) {
 System.out.println("Starting my engine");
 }

 public void visit(Body body) {
 System.out.println("Moving my body");
 }

 public void visit(Car car) {
 System.out.println("Starting my car");
 }
}

public class VisitorDemo {
 public static void main(String[] args) {
 ICarElement car = new Car();
 car.accept(new CarElementPrintVisitor());
 car.accept(new CarElementDoVisitor());
 }
}

Резултат

Visiting front left wheel
Visiting front right wheel
Visiting back left wheel
Visiting back right wheel
Visiting body
Visiting engine
Visiting car
Kicking my front left wheel
Kicking my front right wheel
Kicking my back left wheel
Kicking my back right wheel
Moving my body
Starting my engine
Starting my car

Източници

(defclass auto
 ((elements :initarg :elements)))

(defclass auto-part
 ((name :initarg :name :initform "<unnamed-car-part>")))

(defmethod print-object ((p auto-part) stream)
 (print-object (slot-value p 'name) stream))

(defclass wheel (auto-part))

(defclass body (auto-part))

(defclass engine (auto-part))

(defgeneric traverse (function object other-object))

(defmethod traverse (function (a auto) other-object)
 (with-slots (elements) a
 (dolist (e elements)
 (funcall function e other-object))))

;; do-something visitations

;; catch all
(defmethod do-something (object other-object)
 (format t "don't know how ~s and ~s should interact~%" object other-object))

;; visitation involving wheel and integer
(defmethod do-something ((object wheel) (other-object integer))
 (format t "kicking wheel ~s ~s times~%" object other-object))

;; visitation involving wheel and symbol
(defmethod do-something ((object wheel) (other-object symbol))
 (format t "kicking wheel ~s symbolically using symbol ~s~%" object other-object))

(defmethod do-something ((object engine) (other-object integer))
 (format t "starting engine ~s ~s times~%" object other-object))

(defmethod do-something ((object engine) (other-object symbol))
 (format t "starting engine ~s symbolically using symbol ~s~%" object other-object))

(let ((a (make-instance 'auto
 :elements `(,(make-instance 'wheel :name "front-left-wheel")
 ,(make-instance 'wheel :name "front-right-wheel")
 ,(make-instance 'wheel :name "rear-right-wheel")
 ,(make-instance 'wheel :name "rear-right-wheel")
 ,(make-instance 'body :name "body")
 ,(make-instance 'engine :name "engine")))))
 ;; traverse to print elements
 ;; stream *standard-output* plays the role of other-object here
 (traverse #'print a *standard-output*)

 (terpri);; print newline

 ;; traverse with arbitrary context from other object
 (traverse #'do-something a 42)

 ;; traverse with arbitrary context from other object
 (traverse #'do-something a 'abc))

Резултат

„front-left-wheel“
„front-right-wheel“
„rear-right-wheel“
„rear-right-wheel“
„body“
„engine“
kicking wheel „front-left-wheel“ 42 times
kicking wheel „front-right-wheel“ 42 times
kicking wheel „rear-right-wheel“ 42 times
kicking wheel „rear-right-wheel“ 42 times
don't know how „body“ and 42 should interact
starting engine „engine“ 42 times
kicking wheel „front-left-wheel“ symbolically using symbol ABC
kicking wheel „front-right-wheel“ symbolically using symbol ABC
kicking wheel „rear-right-wheel“ symbolically using symbol ABC
kicking wheel „rear-right-wheel“ symbolically using symbol ABC
don't know how „body“ and ABC should interact
starting engine „engine“ symbolically using symbol ABC

Бележки

Другият other-object е излишен в този случай.
Причината е, че е възможно да използваме анонимна функция която извиква желания метод с лексикално хванат обект:

(defmethod traverse (function (a auto));; other-object removed
 (with-slots (elements) a
 (dolist (e elements)
 (funcall function e))));; from here too

;; ...

 ;; alternative way to print-traverse
 (traverse (lambda (o) (print o *standard-output*)) a)

 ;; alternative way to do-something with
 ;; elements of a and integer 42
 (traverse (lambda (o) (do-something o 42)) a)

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

По този начин всички следи от посетителския модел изчезват, освен мапващата функция, в която няма следа че 2 обекта са били използвани.

Всички следи че има 2 обекта и изпращането на техните типове е в ламбда функция.

Състояние

Като изключим потенциалното подобряване на разделението от опасения, посетителският модел има допълнително предимство пред това просто да извикаме полиморфичен метод:посетителският обект може да има състояние.

Това е изключително полезно в много случаи в които действието извършвано върху обект зависи от предишни действия.

Пример за това е принтиращата мплементация в програмен език (като компилатор или интерпретатор).

Такъв принтиращ обект (имплементиран като посетител, в този случай), ще посети възлите в структура от данни които представляват парсната и обработена програма. Компилаторът ще принтира текстово представяне на програмното дърво. За да направи представянето четимо от хора, компилаторът трябва правилно да представи програмните конструкции и изрази.

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

Сродни модели на дизайн

  • Командващ модел: Подобно на посетителския модел той енкапсулира една или повече функции в обект, за да ги представи пред повиквателя. За разлика от посетителския модел, командващият модел не прилага принципа за обхождане на обектовата структура.
  • Итераторен модел: Този модел дефинира обхождащ принцип подобно на посетителския модел, без обаче да прави разлика между типовете в обхождания обект.

Вижте също

Източници

Външни препратки

Read other articles:

هذه المقالة يتيمة إذ تصل إليها مقالات أخرى قليلة جدًا. فضلًا، ساعد بإضافة وصلة إليها في مقالات متعلقة بها. (يناير 2021) جبل أفادجا الموقع غانا  إحداثيات 7°08′40″N 0°21′18″E / 7.1444444444444°N 0.355°E / 7.1444444444444; 0.355  الارتفاع 885 متر،  و2904 قدم  تعديل مصدري - تعديل   جبل ...

 

Ла-Салзадельяваленс. La Salzadella, ісп. Salsadella Герб {{{official_name}}}ГербFlag of {{{official_name}}}ПрапорМуніципалітетКраїна  ІспаніяАвтономна спільнота ВаленсіяПровінція КастельйонКоординати 40°25′05″ пн. ш. 0°10′30″ сх. д. / 40.418° пн. ш. 0.175° сх. д. / 40.418; 0.175Коор...

 

Das Innere Paulustor auf einer historischen Ansicht. Das Innere Paulustor ist ein historisches Stadttor der Stadt Graz. Es stand von 1355 bis 1846 in der Sporgasse im Bezirk Innere Stadt. Geschichte und Gestaltung Erstmals urkundlich erwähnt wird das sogenannte Innere Paulustor im Jahr 1355. Es war Teil der mittelalterlichen Stadtbefestigung. Sein ehemaliger Standort ist der heutige Platz zwischen dem Palais Saurau und dem Gasthaus zur goldenen Pastete. Namensgebend war die frühere, nahegel...

American college football season 2001 Yale Bulldogs footballConferenceIvy LeagueRecord3–6 (1–6 Ivy)Head coachJack Siedlecki (5th season)Home stadiumYale BowlSeasons← 20002002 → 2001 Ivy League football standings vte Conf Overall Team   W   L     W   L   No. 19 Harvard $   7 – 0     9 – 0   No. 24 Penn   6 – 1     8 – 1   Brown   5 – 2     6 ...

 

Hereford Mappa Mundi, sekitar 1300, Katedral Hereford, Inggris. Mappa mundi (Latin pengucapan Latin: [ˈmappa ˈmʊndiː]; jamak = mappae mundi) adalah peta dunia Abad Pertengahan. Sekitar 1,100 mappae mundi diketahui berasal dari Abad Pertengahan. 900 diantaranya adalah manuskrip bergambar dan sisanya adalah dokumen yang berdiri sendiri.[1] Referensi ^ J. B. Harley (1987); Volume I, p. 286 Daftar pustaka Peter Barber (ed.) (2005) The Map Book, London Anna-Dorothee von den Brinck...

 

زدفينسك الإحداثيات 54°42′00″N 78°40′00″E / 54.7°N 78.666666666667°E / 54.7; 78.666666666667  تاريخ التأسيس 1773  تقسيم إداري  البلد روسيا الإمبراطورية الروسية الاتحاد السوفيتي[1]  خصائص جغرافية ارتفاع 116 متر  عدد السكان  عدد السكان 5402 (2012)  معلومات أخرى منطقة زمنية توق

Failed Icelandic commercial bank This article is about the Icelandic bank up to its failure in 2008. For current bank, see Landsbankinn. For the international school formerly known as Little Angels International School (with the acronym LAIS), see Musashi International School Tokyo. Landsbanki hfTypePrivate (2003-2008)Government-owned corporation (prior to 2003)IndustryBankingFoundedReykjavík, Iceland, 1885 (commenced operations on 1 July 1886)DefunctEntered into receivership 7 October 2008,...

 

Canadian politician For other people named John Lee, see John Lee (disambiguation). John LeeOntario MPPIn office1901–1904Preceded byRobert FergusonSucceeded byPhilip Henry BowyerConstituencyKent East Personal detailsBorn(1845-03-05)March 5, 1845Orford township, Kent County, Canada WestDiedJanuary 16, 1915(1915-01-16) (aged 69)Kent, OntarioPolitical partyLiberalSpouse Rebecca Sophia Attridge ​ ​(m. 1865)​OccupationFarmer John Big John Lee (March 5, 184...

 

10th episode of the 6th season of The Walking Dead The Next WorldThe Walking Dead episodeDaryl and Rick confront Jesus, after he steals the truck they found.Episode no.Season 6Episode 10Directed byKari SkoglandWritten byAngela KangCorey ReedFeatured musicMore Than a Feeling by BostonIf My Heart Was a Car by the Old 97'sOriginal air dateFebruary 21, 2016 (2016-02-21)Running time43 minutesGuest appearances Merritt Wever as Dr. Denise Cloyd Tom Payne as Paul Jesus Rovia Katel...

High Moon StudiosJenisAnak perusahaan dari ActivisionIndustriHiburan interaktifGenreGamingPendahuluSammy StudiosDidirikan2001 (sebagai Sammy Entertainment)KantorpusatSan Diego, California, Amerika SerikatProdukTransformers: War for CybertronTransformers: Fall of CybertronDeadpoolCall of Duty: Advanced Warfare (PS3 & X360)PemilikActivisionKaryawan100IndukSammy Corporation (2001-2004)Sega Sammy (2004-2005)Independent (2005-2006)Sierra Entertainment (2006-2008)Activision (2008-sekarang)Situs...

 

Painting by Élisabeth Vigée Le Brun Marie Antoinette with a RoseArtistÉlisabeth Vigée Le BrunYear1783MediumOil on canvasSubjectMarie AntoinetteDimensions116.8 cm × 88.9 cm (46.0 in × 35.0 in)[1]LocationPalace of Versailles, Versailles Marie Antoinette with a Rose, also known as Marie-Antoinette with the Rose (French: Marie-Antoinette dit « à la Rose »), is an oil painting by the French artist Élisabeth Vigée Le Brun. It wa...

 

1834 Hebron massacrePart of Peasants' revolt in PalestineDateEarly August 1834LocationHebron-Part of Egyptian-ruled provinces of Damascus Eyalet, nominally part of the Ottoman EmpireResult Egyptian victory Massacre of inhabitants Conscription orders carried out Plunder of townBelligerents Egypt Eyalet Rebels of Hebron and Jabal Nablus Qasim and Barqawi clans of Jabal Nablus 'Amr tribe of Hebron HillsCommanders and leaders Ibrahim Pasha Qasim al-Ahmad Abd al-Rahman 'Amr 'Isa al-BarqawiStrength...

Wakil Bupati LuwuPetahanaLowongsejak 6 April 2023KediamanRumah Jabatan Bupati LuwuMasa jabatan5 tahunDibentuk2004Pejabat pertamaBahrum Daido Berikut ini adalah daftar wakil bupati Luwu yang menjabat sejak pembentukannya pada tahun 2004. No Wakil Bupati[1] Mulai menjabat Akhir menjabat Prd. Ket. Bupati 1 Ir. H.Bahrum Daido M.Si 2004 2008 19 Drs. H.Basmin MattayangM.Pd Lowong 2008 2009 [2] Ir. H.Bahrum Daido M.Si 2 Syukur Bijak 2009 2014 20 Ir. H.A. Mudzakkar 3 H. Amru Sahe...

 

Monster Games, Inc.公司類型游戏开发者 成立1996年11月,​27年前​(1996-11)總部美国明尼苏达州诺斯菲尔德市产业电子游戏產品电子游戏网站www.mgiracing.com 怪兽游戏工作室(英語:Monster Games, Inc.) 是一家位于美国明尼苏达州诺斯菲尔德市的独立的电子游戏开发商,成立于1996年。公司为人熟知的作品是作为Wii平台的首发游戏之一的《激情卡车(英语:Excite Truck)》和...

 

Roller coaster at Great America parks This article is about the roller coasters at the former Marriott's Great America parks. For Wonderland Sydney ride, see The Demon (Wonderland Sydney). This article needs additional citations for verification. Please help improve this article by adding citations to reliable sources. Unsourced material may be challenged and removed.Find sources: Demon roller coaster – news · newspapers · books · scholar · JSTOR ...

French actor This article needs additional citations for verification. Please help improve this article by adding citations to reliable sources. Unsourced material may be challenged and removed.Find sources: Jean Gabin – news · newspapers · books · scholar · JSTOR (September 2015) (Learn how and when to remove this template message) Jean GabinJean Gabin as Jules Maigret in 1958BornJean-Alexis Moncorgé17 May 1904Paris, FranceDied15 November 1976(1976-1...

 

1975 Australian filmScobie MaloneDVD coverDirected byTerry OhlssonWritten byCasey RobinsonGraham WoodlockBased onHelga's Webby Jon ClearyProduced byCasey RobinsonStarringJack ThompsonJudy MorrisCinematographyKeith LambertEdited byBill StaceyMusic byPeter ClarkeProductioncompaniesKingcroft AustraliaAustralian Film Development CorporationRelease date 3 October 1975 (1975-10-03) Running time98 minutesCountryAustraliaLanguageEnglishBudgetAU$300,000[1][2] Scobie Malo...

 

Private college in Oriental Mindoro, Philippines This article needs additional citations for verification. Please help improve this article by adding citations to reliable sources. Unsourced material may be challenged and removed.Find sources: Divine Word College of Calapan – news · newspapers · books · scholar · JSTOR (January 2022) (Learn how and when to remove this template message) Divine Word College of CalapanDalubhasaang Banal na Salita ng Calap...

Untuk pengertian lain, lihat Dearborn. Markas Ford World di Dearborn dikenal sebagai Glass House Dearborn ialah sebuah kota terbesar ke-10 di Detroit Raya, Wayne County, Michigan, Amerika Serikat. Pada tahun 2000 kota ini berpenduduk 97.775 jiwa. Dearborn adalah kampung halaman Henry Ford dan markas besar Ford Motor Company. Kota ini dinamai menurut Jenderal Henry Dearborn, tokoh militer dan politik Amerika Serikat abad ke-18-19. University of Michigan dan Henry Ford Community College terleta...

 

  Zorzal abisinio Estado de conservaciónPreocupación menor (UICN 3.1)[1]​TaxonomíaReino: AnimaliaFilo: ChordataClase: AvesOrden: PasseriformesFamilia: TurdidaeGénero: TurdusEspecie: T. abyssinicusJ.F. Gmelin, 1789[2]​Sinonimia Turdus olivaceus abyssinicus [editar datos en Wikidata] El zorzal abisinio (Turdus abyssinicus) es una especie de ave paseriforme de la familia Turdidae propia del este de África. Anteriormente se consideraba conespecífico del zorza...

 

Strategi Solo vs Squad di Free Fire: Cara Menang Mudah!