当前位置:网站首页>Visitor mode
Visitor mode
2022-07-25 22:12:00 【At the beginning of dawn】
1. Introduction
- To move the brick N year , It's not easy to gather hundreds of thousands , Opened a marinated meat shop ( Forgive the author for being a glutton )
- To attract customers , The introduction of the membership system : Ordinary original price , Silver members 8.8 fold , Platinum members 8 fold
- The ancestral secret recipe in the store , Just make stewed duck 、 Chicken feet 、 Stewed pork head
- Born with bricks , I'm sorry if I don't write a cashier program myself
2. The original cashier program
2.1 Membership level
According to the demand , Define an enumeration class
MembershipLevelTo record membership gradespublic enum MembershipLevel { NORMAL, SILVER, PLATINUM }
2.2 All kinds of marinated meat in the store
There are three kinds of marinated meat in the shop , Each kind of marinated meat has two properties : Name and unit price , Two operations : Get the name 、 Return the discounted unit price according to the member level
Abstract out Meat Interface :
public interface Meat { double getPrice(MembershipLevel level); String getName(); }Realize three kinds of marinated meat : Stewed duck 、 Chicken feet 、 Stewed pig head
public class CookedDark implements Meat { private final double price; public CookedDark(double price) { this.price = price; } @Override public double getPrice(MembershipLevel level) { if (MembershipLevel.NORMAL == level) { return price; } else if (MembershipLevel.SILVER == level) { return price * 0.88; } else { return price * 0.8; } } @Override public String getName() { return " Stewed duck "; } } public class CookedChickenFeet implements Meat { private final double price; public CookedChickenFeet(double price) { this.price = price; } @Override public double getPrice(MembershipLevel level) { if (MembershipLevel.NORMAL == level) { return price; } else if (MembershipLevel.SILVER == level) { return price * 0.88; } else { return price * 0.8; } } @Override public String getName() { return " bittern chicken claws "; } } public class CookedPigHead implements Meat { private final double price; public CookedPigHead(double price) { this.price = price; } @Override public double getPrice(MembershipLevel level) { if (MembershipLevel.NORMAL == level) { return price; } else if (MembershipLevel.SILVER == level) { return price * 0.88; } else { return price * 0.8; } } @Override public String getName() { return " Stewed pig head "; } }
2.2 Marinated meat shop
There are all kinds of marinated meat in the marinated meat shop , Calculate the price according to the customer's purchase and membership level
public class CookedFoodShop { private Map<String, Meat> meatMap; public CookedFoodShop() { meatMap = new HashMap<>(); } public CookedFoodShop addMeat(Meat meat) { meatMap.put(meat.getName(), meat); return this; } public void calculatePrice(Map<String, Double> weight, MembershipLevel level) { double total = 0; System.out.println("Membership level: " + level); System.out.println("Meat Price Weight Cost"); for (Map.Entry<String, Double> item : weight.entrySet()) { Meat meat = meatMap.get(item.getKey()); double cost = item.getValue() * meat.getPrice(level); total += cost; System.out.printf("%s %.2f %.2f %.2f\n", meat.getName(), meat.getPrice(level), item.getValue(), cost); } System.out.printf("Total cost: %.2f\n", total, total * 0.88); } }
2.3 The marinated meat shop opened
Create your own marinated meat shop , After setting the price of different marinated meat , You can open the door , Use your own program to calculate the price
public class CalculatePrice { public static void main(String[] args) { // Create your own marinated meat shop CookedFoodShop shop = new CookedFoodShop() .addMeat(new CookedDark(38)) .addMeat(new CookedChickenFeet(42)) .addMeat(new CookedPigHead(35)); // Input the weight of all kinds of marinated meat Map<String, Double> weight = new HashMap<>(); weight.put(" bittern chicken claws ", 2.4); weight.put(" Stewed pig head ", 1.5); // Calculate the price shop.calculatePrice(weight, MembershipLevel.SILVER); } }Final , The consumption of a silver member is as follows :

3. Membership levels have been enriched
3.1 An awkward situation
- Because of the ancestral craft , Attracted a lot of customers , At this time, the membership level is getting richer and richer
- Big customers like the company canteen , direct 75 fold ; Franchisee , direct 7 fold
- Now I have to change the code again
- First , Need to be rich
MembershipLevelMember level in - secondly , Modify the marinated meat , Enrich
getPrice()Support for various member levels in the method : Or enrichif - elseBranch , Or useswitchsentence
- First , Need to be rich
- It's really troublesome : The addition of new member level , Need to modify all the marinated meat
getPrice()Method , Violated the famous Opening and closing principle ( Open to expansion , Turn off for changes ).
3.2 introduce visitor Pattern
Observe carefully , It's not hard to see : In the whole demand , The type of marinated meat remains unchanged , What changes is the membership level , And member price calculation logic
getPrice()MethodIf you can separate the logic of member price calculation from the marinated meat , When the membership level changes , The existing marinated meat can be modified
First , Create a
VisitorInterface , It contains different kinds of marinated meat , Get the original price of marinated meat and calculate the member pricevisit()Methodpublic interface Visitor { double visit(CookedDark dark); double visit(CookedChickenFeet feet); double visit(CookedPigHead pigHead); // Get membership level , Help print tickets MembershipLevel getLevel(); }next , to update Meat Interface
getPrice()Method , Remove the calculation logic of different member prices , Return the original price directly// take Meat Interface getPrice() The method changes as follows : double getPrice(); // In all kinds of marinated meat , Realization getPrice() Method , Return the original price directly @Override public double getPrice() { return this.price; }then , be based on Visitor Interface , Realize the corresponding of different member levels Visitor class
public class NormalVisitor implements Visitor { private static final MembershipLevel level = MembershipLevel.NORMAL; @Override public double visit(CookedDark dark) { return dark.getPrice(); } @Override public double visit(CookedChickenFeet feet) { return feet.getPrice(); } @Override public double visit(CookedPigHead pigHead) { return pigHead.getPrice(); } @Override public MembershipLevel getLevel() { return level; } } public class SilverVisitor implements Visitor { private static final MembershipLevel level = MembershipLevel.SILVER; @Override public double visit(CookedDark dark) { return dark.getPrice() * 0.88; } @Override public double visit(CookedChickenFeet feet) { return feet.getPrice() * 0.88; } @Override public double visit(CookedPigHead pigHead) { return pigHead.getPrice() * 0.88; } @Override public MembershipLevel getLevel() { return level; } } public class PlatinumVisitor implements Visitor { public static final MembershipLevel level = MembershipLevel.PLATINUM; @Override public double visit(CookedDark dark) { return dark.getPrice() * 0.8; } @Override public double visit(CookedChickenFeet feet) { return feet.getPrice() * 0.8; } @Override public double visit(CookedPigHead pigHead) { return pigHead.getPrice() * 0.8; } @Override public MembershipLevel getLevel() { return level; } }Now? , Whole visitor Here comes the key point of the pattern : How to combine marinated meat with Visitor Class is associated with ?
- by Meat Add an interface
double accept(Visitor visitor)Method - Marinated meat is realized
accept()When the method is used , Pass yourself to visitor Of visit() Method
public class CookedDark implements Meat { ... // Other code omissions @Override public double accept(Visitor visitor) { return visitor.visit(this); } } public class CookedChickenFeet implements Meat { ... // Other code omissions @Override public double accept(Visitor visitor) { return visitor.visit(this); } } public class CookedPigHead implements Meat { ... // Other code omissions @Override public double accept(Visitor visitor) { return visitor.visit(this); } }- here , Stewed duck + Take silver members for example , Formed such a relationship chain :

- by Meat Add an interface
modify
CookedFoodShop.calculatePrice()Methodpublic void calculatePrice(Map<String, Double> weight, Visitor visitor) { double total = 0; System.out.println("Membership level: " + visitor.getLevel()); System.out.println("Meat Price Weight Cost"); for (Map.Entry<String, Double> item : weight.entrySet()) { Meat meat = meatMap.get(item.getKey()); double cost = item.getValue() * meat.accept(visitor); total += cost; System.out.printf("%s %.2f %.2f %.2f\n", meat.getName(), meat.accept(visitor), item.getValue(), cost); } System.out.printf("Total cost: %.2f\n", total, total * 0.88); }Calculate the fees to be paid by Platinum members
public class CalculatePrice { public static void main(String[] args) { // Create your own marinated meat shop CookedFoodShop shop = new CookedFoodShop() .addMeat(new CookedDark(38)) .addMeat(new CookedChickenFeet(42)) .addMeat(new CookedPigHead(35)); // Input the weight of all kinds of marinated meat Map<String, Double> weight = new HashMap<>(); weight.put(" bittern chicken claws ", 2.4); weight.put(" Stewed pig head ", 1.5); // Calculate the price shop.calculatePrice(weight, new PlatinumVisitor()); } }The contents of the ticket are as follows :

4. visitor Pattern
4.1 Concept
- In code development , We often encounter such scenes :
- A composite object , Contains a set of similar and fixed Element
- If necessary for this group Element Introduce new operations , You need to modify all Element( The new method ) And composite objects ( quote Element New method of )
- This is the code design , Obviously violates the opening and closing principle ( Open to expansion , Turn off for changes )
- At this time , Consider using visitors (visitor) Pattern , To define a new operation without modifying the existing object structure
- visitor The pattern belongs to Behavioral design patterns , There are two important ways :
- Element Medium
accept(visitor)Method : Can receive different types of visitors (visitor), And execute in the methodvisitor.visit(this) - Visitor Medium
visit(concreteElement)Method : Visit specific Element, Realize to Element Operation logic of ; When you need to Element When a new operation is introduced , Just add specific Visitor class - stay
accept()Method executionvisitor.visit(this), Cleverly put Visitor And Element Connect , Realizeddouble dispatch
- Element Medium
- From the above description, it is not difficult to find :visitor The mode meets the opening and closing principle , It realizes the separation of operation logic and the operated object
- I'm right
double dispatch:- dispatch 1: adopt element.accept(visitor), take visitor dispatch to element
- dispatch 2: through visitor.visit(this), take element dispatch to visitor, To achieve the right element Some kind of operation
- Compared to direct call element.operation(), Such call logic can be called
double dispatch
4.2 UML chart
visitor Mode UML The graph is as follows :

Client:client yes visitor Consumers of classes in the pattern , It will be responsible for visitor Object injection into composite objects (Object Structure) in
Object Structure: Traverse the contained Element object , By calling Element Object's
accept()Method , take visitor Objects act on each Element- Their own implementation of the marinated meat shop cashier program code , It does not clearly reflect the right Element Traversal of objects
- About visitor In the sample code of the pattern , This is very obvious
public void accept(Visitor v) { for (Element e : this.elements) { e.accept(v); } }Visitor: Interface or abstract class , For each of the compound objects Element Defines the corresponding
visit(element)MethodConcreteVisitor : For different types of visitor, Must be realized Visitor Medium
visit()Method , stayvisit()Method is defined for Element Object operation logicElement: Interface or abstract class , There is an acceptance visitor Of
accept(visitor)Method , yes visitor visit Element The entrance to the objectConcreteElement: Concrete Element, Must be realized Element Medium
accept()Method , adopt this keyword (visitor.visit(this)) Pass yourself to visitor Of visit() Method , So as to achievedouble dispatch
4.3 Advantages and disadvantages
4.3.1 advantage
- Combine the operation logic with the operated object (Element) Separate , So that the new operation does not need to modify the existing Element, accord with Opening and closing principle
- Yes Element When introducing a new operation , Just add the corresponding Visitor class , It has good expansibility
4.3.2 shortcoming
- When adding Element when , Code maintenance becomes difficult , It also violates the opening and closing principle
- Modify the existing Visitor class , To add new Element Of visit() Method
- If there are many Visitor class , This will be a very heavy work
- Visitor Direct access Element, bring Element The relevant business logic is exposed to all visitor, Contrary to The Demeter principle
- In order to achieve different Element Of
Differential treatment, Direct access to specific classes , Not interfaces or abstract classes , A violation of the The principle of Dependence Inversion
5. Postscript
- Reference link :
- Visitor mode is enough
- Visitor Design Pattern in Java
- Visitor design pattern
- Java Advanced design pattern 10 ---- Visitor model and mediator model ( Yes accept() Detailed description of the method , Very intimate )
- On the principle of design pattern : Opening and closing principle OCP
边栏推荐
- Animation curves are used every day. Can you make one by yourself? After reading this article, you will!
- golang : MVC之models
- QML module not found
- Guiding principles of information security construction
- JMeter websocket interface test
- Output Yang Hui triangle with two-dimensional array
- Sofa weekly | open source person - Niu Xuewei, QA this week, contributor this week
- What is the difference between minor GC and full GC?
- Having met a tester with three years' experience in Tencent, I saw the real test ceiling
- 核电站在席卷欧洲的热浪中努力保持安全工作
猜你喜欢

Wechat applet application development competition works comprehensive development record - Jinlu cultural tourism (cloud development - Overview)

动画曲线天天用,你能自己整一个吗?看完这篇你就会了!

6-17 vulnerability exploitation - deserialization remote command execution vulnerability

Tfrecord write and read

What have I experienced to become a harder tester than development?
![[assembly language 01] basic knowledge](/img/df/d586288b8f41211141bc4e2bca20cf.png)
[assembly language 01] basic knowledge

How to implement an app application to limit users' time use?

Don't vote, software testing posts are saturated

Jmeter--- set proxy recording request

Ansible+cronab batch deployment patrol
随机推荐
【C语法】void*浅说
Dovecot set mailbox quota
Some summary about function
在进行自动化测试,遇到验证码的问题,怎么办?
6-17漏洞利用-反序列化远程命令执行漏洞
Summary of function test points of wechat sending circle of friends on mobile terminal
MySQL - subquery - column subquery (multi row subquery)
JSP nine built-in objects
如何实现一个App应用程序,限制用户时间使用?
Golang: MVC models
Don't vote, software testing posts are saturated
The dragon lizard exhibition area plays a new trick this time. Let's see whose DNA moved?
Wechat applet application development competition works comprehensive development record - Jinlu cultural tourism (cloud development - Overview)
Jmeter--- set proxy recording request
Minor GC 和 Full GC 有什么不同呢?
All you want to know about interface testing is here
Leetcode 106. 从中序与后序遍历序列构造二叉树
win10搭建flutter环境踩坑日记
Having met a tester with three years' experience in Tencent, I saw the real test ceiling
动画曲线天天用,你能自己整一个吗?看完这篇你就会了!