?需求:店鋪采購了一批水果(蘋果及橘子),現(xiàn)在市場監(jiān)督局來店里檢查過期的水果。
public class Fruit {
private String name;
private Date pickDate;
public Fruit(String name, Date pickDate) {
this.name = name;
this.pickDate = pickDate;
}
public String getName() {
return name;
}
public Date getPickDate() {
return pickDate;
}
@Override
public String toString() {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm");
return "{" +
"name='" + name + '\'' +
", 采摘日期=" + simpleDateFormat.format(pickDate) +
'}';
}
}
public class Apple extends Fruit{
public Apple(String name, Date pickDate) {
super(name, pickDate);
}
}
public class Orange extends Fruit{
public Orange(String name, Date pickDate) {
super(name, pickDate);
}
}
public class FruitShop {
private final String name;
public FruitShop(String name) {
this.name = name;
}
private final List<Fruit> fruitList = new ArrayList<>();
public List<Fruit> getFruitList() {
return fruitList;
}
public String getName() {
return name;
}
}
public class FruitMarket {
public static void main(String[] args) throws ParseException {
FruitShop myShop = new FruitShop("老果農(nóng)");
purchaseFruit(myShop);
expCheck("市場監(jiān)督局",myShop); //市場監(jiān)督局檢查過期水果:蘋果過期時間5天,橘子過期時間10天
expCheck("城管",myShop); //城管檢查過期水果:蘋果過期時間3天,橘子過期時間6天
newArrival(myShop);//新品上市水果
}
// 采購水果
public static void purchaseFruit(FruitShop shop) throws ParseException {
List<Fruit> fruitList = shop.getFruitList();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm");
fruitList.add(new Apple("富士康",simpleDateFormat.parse("2023-06-21 12:00")));
fruitList.add(new Orange("贛南臍橙", simpleDateFormat.parse("2023-05-25 18:00")));
fruitList.add(new Orange("韶關(guān)帝王橘", simpleDateFormat.parse("2023-6-18 21:00")));
fruitList.add(new Apple("王掌柜",simpleDateFormat.parse("2023-06-11 12:00")));
}
// 過期檢查
public static void expCheck(String development,FruitShop shop) {
int appleExpDay = 0, orangeExpDay = 0;
if ("市場監(jiān)督局".equals(development)) {
appleExpDay = 5;
orangeExpDay = 10;
} else if ("城管".equals(development)) {
appleExpDay = 2;
orangeExpDay = 4;
}
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date());
calendar.add(Calendar.DATE,-appleExpDay);
Date appleExpDate = calendar.getTime();
calendar.setTime(new Date());
calendar.add(Calendar.DATE,-orangeExpDay);
Date orangeExpDate = calendar.getTime();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm");
System.out.println(development + "檢查 蘋果過期天數(shù):" + appleExpDay + " 橘子過期天數(shù):" + orangeExpDay + " 檢查時間:" + dateFormat.format(new Date()) + " " + shop.getName());
for (Fruit fruit : shop.getFruitList()) {
boolean exp = false;
if (fruit instanceof Apple) {
if (appleExpDate.after(fruit.getPickDate())) exp = true;
} else if (fruit instanceof Orange) {
if (orangeExpDate.after(fruit.getPickDate())) exp = true;
}
if (exp) System.out.println(fruit + " 過期! ");
}
System.out.println("-------");
}
}
如果此時再添加一個操作:找出新品上市的水果。
// 新品上市 蘋果為2023-06-20 后采摘, 橘子為2023-06-21后采摘
public static void newArrival(FruitShop shop) throws ParseException {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
Date appleNewDate = simpleDateFormat.parse("2023-06-20");
Date orangeNewDate = simpleDateFormat.parse("2023-06-21");
for (Fruit fruit : shop.getFruitList()) {
boolean isNew = false;
if (fruit instanceof Apple) {
if(appleNewDate.before(fruit.getPickDate())) isNew = true;
} else if (fruit instanceof Orange) {
if(orangeNewDate.before(fruit.getPickDate())) isNew= true;
}
if (isNew) System.out.println(fruit.getName() + " 新品上市");
}
}
上述代碼中,F(xiàn)ruitMarket為應(yīng)付新增操作,增加了相應(yīng)的方法來滿足要求,但這樣破壞了FruitMarket的結(jié)構(gòu)。
訪問者模式
本質(zhì)是將數(shù)據(jù)結(jié)構(gòu)和數(shù)據(jù)操作分離,通過定義一個訪問者對象,實現(xiàn)對數(shù)據(jù)結(jié)構(gòu)中各個元素的訪問和處理,從而達到解耦和靈活性的目的。
圖 訪問者UML
Visitor 抽象訪問者
為ObjectStructure對象結(jié)構(gòu)中的每一個Element都聲明一個Visit操作。
ConcreteVisitor具體訪問者
Visitor的實現(xiàn),實現(xiàn)要真正被添加到對象結(jié)構(gòu)中的功能。
ObjectStructure 對象結(jié)構(gòu)
通常包含多個被訪問的對象,可以是一個復合或是一個集合。
Element 抽象元素
為Visitor聲明一個accept方法,實現(xiàn)元素與訪問者的綁定。
ConcreteElement 具體元素
對象結(jié)構(gòu)體中具體的對象,是被訪問的對象。
/**
* 水果元素抽象類
*/
public abstract class FruitElement {
private String name;
private Date pickDate;
public FruitElement(String name, Date pickDate) {
this.name = name;
this.pickDate = pickDate;
}
public String getName() {
return name;
}
public Date getPickDate() {
return pickDate;
}
public abstract void accept(Visitor visitor);
@Override
public String toString() {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm");
return "{" +
"name='" + name + '\'' +
", 采摘日期=" + simpleDateFormat.format(pickDate) +
'}';
}
}
public class AppleElement extends FruitElement{
public AppleElement(String name, Date pickDate) {
super(name, pickDate);
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
public class OrangeElement extends FruitElement{
public OrangeElement(String name, Date pickDate) {
super(name, pickDate);
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
/**
* 訪問者
*/
public interface Visitor {
void visit(AppleElement apple);
void visit(OrangeElement orange);
}
/**
* 政府檢查部門
*/
public class DevelopmentVisitor implements Visitor{
private final int appleExpDay;
private final int orangeExpDay;
public DevelopmentVisitor(String name, int appleExpDay, int orangeExpDay) {
this.appleExpDay = appleExpDay;
this.orangeExpDay = orangeExpDay;
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm");
System.out.println(name + " 檢查過期水果, 蘋果過期天數(shù):" + appleExpDay + ",橘子過期天數(shù):" +
orangeExpDay + " 檢查時間:" + dateFormat.format(new Date()));
}
@Override
public void visit(AppleElement apple) {
expCheck(apple, appleExpDay);
}
@Override
public void visit(OrangeElement orange) {
expCheck(orange,orangeExpDay);
}
private void expCheck(FruitElement fruit,int expDay) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date());
calendar.add(Calendar.DATE,-appleExpDay);
Date appleExpDate = calendar.getTime();
if (appleExpDate.after(fruit.getPickDate())) {
System.out.println(fruit.getName() + " 過期!");
}
}
}
/**
* 新品上市檢查
*/
public class NewArrivalVisitor implements Visitor {
private final Date appleNewDate;
private final Date orangeNewDate;
public NewArrivalVisitor(Date appleNewDate, Date orangeNewDate) {
this.appleNewDate = appleNewDate;
this.orangeNewDate = orangeNewDate;
System.out.println("新品上市檢查");
}
@Override
public void visit(AppleElement apple) {
if (appleNewDate.before(apple.getPickDate())) {
System.out.println(apple.getName() + " 新品上市");
}
}
@Override
public void visit(OrangeElement orange) {
if (orangeNewDate.before(orange.getPickDate())) {
System.out.println(orange.getName() + " 新品上市");
}
}
}
/**
* 水果店結(jié)構(gòu)
*/
public class FruitShopStructure {
private final List<FruitElement> fruitList = new ArrayList<>();
public List<FruitElement> getFruitList() {
return fruitList;
}
}
public class FruitMarket2 {
public static void main(String[] args) throws ParseException {
//新開一家水果店
FruitShopStructure shop = new FruitShopStructure();
purchaseFruit(shop);
System.out.println("------------------------------");
//市場監(jiān)督局來檢查是否存在過期水果
DevelopmentVisitor devVisitor1 = new DevelopmentVisitor("市場監(jiān)督局", 5, 10);
for (FruitElement element : shop.getFruitList()) {
element.accept(devVisitor1);
}
System.out.println("------------------------------");
//農(nóng)業(yè)局來檢查是否存在過期水果
DevelopmentVisitor devVisitor2 = new DevelopmentVisitor("農(nóng)業(yè)局", 2, 4);
for (FruitElement element : shop.getFruitList()) {
element.accept(devVisitor2);
}
System.out.println("------------------------------");
//檢查新品上市
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
NewArrivalVisitor newArrivalVisitor = new NewArrivalVisitor(simpleDateFormat.parse("2023-06-20"), simpleDateFormat.parse("2023-06-21"));
for (FruitElement element : shop.getFruitList()) {
element.accept(newArrivalVisitor);
}
}
// 采購水果
public static void purchaseFruit(FruitShopStructure shop) throws ParseException {
List<FruitElement> fruitList = shop.getFruitList();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm");
fruitList.add(new AppleElement("富士康",simpleDateFormat.parse("2023-06-21 12:00")));
fruitList.add(new OrangeElement("贛南臍橙", simpleDateFormat.parse("2023-05-25 18:00")));
fruitList.add(new OrangeElement("韶關(guān)帝王橘", simpleDateFormat.parse("2023-6-18 21:00")));
fruitList.add(new AppleElement("王掌柜",simpleDateFormat.parse("2023-06-11 12:00")));
}
}
圖 運行結(jié)果?
以上代碼是用訪問者模式實現(xiàn)文章開頭處的需求。
優(yōu)點
- 好的擴展性,能夠在不修改對象結(jié)構(gòu)中的元素的情況下,為對象結(jié)構(gòu)中的元素添加新的功能。
- 好的復用性,可以通過訪問者來定義整個對象結(jié)構(gòu)通用的功能,從而提高復用程度。
- 分離無關(guān)行為,通過訪問者來分離無關(guān)的行為,把相關(guān)行為封裝在一起,構(gòu)成一個訪問者,這樣每個訪問者的功能都比較單一。
缺點
1)對象結(jié)構(gòu)變化很困難,不適用于對象結(jié)構(gòu)中的類經(jīng)常變化的情況,當對象結(jié)構(gòu)發(fā)生改變時,訪問者的接口及實現(xiàn)都要做相應(yīng)改變。
2)破壞封裝,訪問者模式通常需要對象結(jié)構(gòu)開放內(nèi)部數(shù)據(jù)給訪問者和ObjectStructure,這破壞了對象的封裝性。
應(yīng)用場景
1)需要對一個復雜的數(shù)據(jù)結(jié)構(gòu)進行操作,并且這些操作可能需要根據(jù)不同的元素類型進行變化。文章來源:http://www.zghlxwxcb.cn/news/detail-498370.html
2)當數(shù)據(jù)結(jié)構(gòu)中的元素種類相對穩(wěn)定,但可能需要新增一些新的操作時。文章來源地址http://www.zghlxwxcb.cn/news/detail-498370.html
到了這里,關(guān)于設(shè)計模式—訪問者模式的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!