设计模式之访问者模式和中介者模式

上次学了解释器模式和迭代器模式,都不是用得很多,而且都比较好理解,就不水博客了

访问者模式

描述:封装一些施加于某种数据结构元素之上的操作。一旦这些操作需要修改,接受这个操作的数据结构可以保持不变。访问者模式适用于数据结构相对未定的系统,它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由的演化。

访问者模式

访问者模式的目的是要把处理从数据结构分离出来,有比较稳定的数据结构,又有易于变化的算法的话,使用访问者模式是合适的,因为它使得算法操作的增加变得容易。其缺点是增加新的数据结构变得困难。访问者模式比较复杂,当真正需要它的时候才考虑使用它。

访问者模式的5和角色

  • Visitor(抽象访问者)角色:声明了一个或者多个方法操作,形成所有的具体访问者角色必须实现的接口。

  • ConcreteVisitor(具体访问者)角色:实现抽象访问者所声明的接口,也就是抽象访问者所声明的各个访问操作。

  • Element(抽象元素):声明一个接受操作,接受一个访问者对象作为一个参数。

  • ConcreteElement(具体元素):实现了抽象节点所规定的接受操作。

  • ObjectStructure(结构对象):有如下的责任,可以遍历结构中的所有元素。

在本例中,把厕所当做抽象元素,具体元素有男厕所和女厕所,由此可见数据结构时稳定的,因为生理性别只有男女之分。进了厕所可以解大便解小便,洗手,抽烟,想干啥干啥。因此操作是多变的,这些操作都相当于是“放松”,把“放松”抽象成Visitor, 用代码模拟如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
//Visitor
abstract class Rest {
public abstract void getManRest(ManRoom manRoom);

public abstract void getWomanRest(WomanRoom womanRoom);
}

//ConcreteVisitor
class Stool extends Rest {
@Override
public void getManRest(ManRoom manRoom) {
System.out.println(manRoom.getClass().getSimpleName() + "上大便");
}

@Override
public void getWomanRest(WomanRoom womanRoom) {
System.out.println(womanRoom.getClass().getSimpleName() + "上大便");
}
}

class Pee extends Rest {
@Override
public void getManRest(ManRoom manRoom) {
System.out.println(manRoom.getClass().getSimpleName() + "上小便");
}

@Override
public void getWomanRest(WomanRoom womanRoom) {
System.out.println(womanRoom.getClass().getSimpleName() + "上小便");
}
}

//Element
abstract class Toilet {
public abstract void accept(Rest rest);
}

//ConcreteElementA
class ManRoom extends Toilet {
@Override
public void accept(Rest rest) {
rest.getManRest(this);
}
}

//ConcreteElementB
class WomanRoom extends Toilet {
@Override
public void accept(Rest rest) {
rest.getWomanRest(this);
}
}

//ObjectStructure
class ToiletManager {
private List<Toilet> elements = new ArrayList<>();

public void attach(Toilet toilet) {
elements.add(toilet);
}

public void detach(Toilet toilet) {
elements.remove(toilet);
}

public void display(Rest rest) {
for (Toilet toilet : elements) {
toilet.accept(rest);
}
}
}

主程序

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Visitor {
public static void main(String[] args) {
ToiletManager toiletManager = new ToiletManager();
toiletManager.attach(new ManRoom());
toiletManager.attach(new WomanRoom());

Stool stool = new Stool();
toiletManager.display(stool);

Pee pee = new Pee();
toiletManager.display(pee);
}
}

中介者模式

描述:包装了一系列对象相互作用的方式,使得这些对象不必相互明显作用,从而使它们可以松散偶合。当某些对象之间的作用发生改变时,不会立即影响其他的一些对象之间的作用,保证这些作用可以彼此独立的变化。

中介者模式

在开发中会遇到这样的场景:尽管将系统分割成许多对象通常可增加其可复用性,但是对象间相互连接的激增又会降低其可复用性了,大量的连接使得一个对象不可能在没有其他对象的支持下工作,系统表现为一个不可分割的整体,所以,对系统的行为进行任何较大的改动就十分困难了,这就是中介者模式解决问题的背景。

中介者模式的4个角色

  • Mediator(抽象中介者):它定义一个接口,该接口用于与各同事对象之间进行通信。

  • ConcreteMediator(具体中介者):它是抽象中介者的子类,通过协调各个同事对象来实现协作行为,它维持了对各个同事对象的引用。

  • Colleague(抽象同事类):它定义各个同事类公有的方法,并声明了一些抽象方法来供子类实现,同时它维持了一个对抽象中介者类的引用,其子类可以通过该引用来与中介者通信。

  • ConcreteColleague(具体同事类):它是抽象同事类的子类;每一个同事对象在需要和其他同事对象通信时,先与中介者通信,通过中介者来间接完成与其他同事类的通信;在具体同事类中实现了在抽象同事类中声明的抽象方法。

中介者模式的核心在于中介者类的引入,在中介者模式中,中介者类承担了两方面的职责:

  • 中转作用(结构性):通过中介者提供的中转作用,各个同事对象就不再需要显式引用其他同事,当需要和其他同事进行通信时,可通过中介者来实现间接调用。该中转作用属于中介者在结构上的支持。

  • 协调作用(行为性):中介者可以更进一步的对同事之间的关系进行封装,同事可以一致的和中介者进行交互,而不需要指明中介者需要具体怎么做,中介者根据封装在自身内部的协调逻辑,对同事的请求进行进一步处理,将同事成员之间的关系行为进行分离和封装。

在本例中,买家和买家交易时使用移动支付,以前是使用纸币进行面对面交易。现在可以通过支付宝、微信转账交易,这些移动支付平台就相当于中介者。而支付宝、微信就是具体中介者了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
//Mediator
abstract class MobilePayment {
public abstract void transfer(Account account, double amount);
}

//Colleague
abstract class Account {
protected MobilePayment mobilePayment;

public Account(MobilePayment mobilePayment) {
this.mobilePayment = mobilePayment;
}
}

//ConcreteColleague
class Seller extends Account {
public Seller(MobilePayment mobilePayment) {
super(mobilePayment);
}

public void transfer(double amount) {
mobilePayment.transfer(this, amount);
}

public void notify(double amount) {
System.out.println("卖家收到 " + amount + " 元");
}
}

class Buyer extends Account {
public Buyer(MobilePayment mobilePayment) {
super(mobilePayment);
}

public void transfer(double amount) {
mobilePayment.transfer(this, amount);
}

public void notify(double amount) {
System.out.println("买家收到 " + amount + " 元");
}
}

//ConcreteMediator
class Alipay extends MobilePayment {
private Buyer buyer;
private Seller seller;

public void setBuyer(Buyer buyer) {
this.buyer = buyer;
}

public void setSeller(Seller seller) {
this.seller = seller;
}

@Override
public void transfer(Account account, double amount) {
if (account instanceof Buyer) {
seller.notify(amount);
} else {
buyer.notify(amount);
}
}
}

主程序

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Mediator {
public static void main(String[] args) {
Alipay alipay = new Alipay();

Buyer buyer = new Buyer(alipay);
Seller seller = new Seller(alipay);

alipay.setBuyer(buyer);
alipay.setSeller(seller);

buyer.transfer(999);
}
}

中介者模式很容易在系统中应用,也很容易误用。当系统中出现了“多对多”交互复杂的对象群时,不要急于使用中介者模式,而要先反思你的系统在设计上是不是合理。

优点:Mediator的出现减少了各个Colleague的耦合,使得可以独立改变和复用各个Colleague,由于把对象如何协作进行了抽象,将中介者作为一个独立的概念并将其封装在一个对象中,这样关注的对象就从对象各自本身转移到它们之间的交互上来,也就是以更宏观的角度去看待系统。

缺点:由于ConcreteMediator控制了集中化,于是就把交互复杂性变为中介者复杂性,这就使得中介者会变得比任何一个ConcreteColleague都复杂。

参考资料:

0%