→23种设计模式大纲
访问者模式、中介者模式、解释器模式
1)访问者模式
访问者模式:Visitor Pattern
访问者模式在行为型设计模式里面算的上是最复杂的,使用也较少,但在某些特定场合很有必要。
主要角色:
- 抽象Element接口:定义一个能被 Visitor (访问者类) 访问的方法;
- 具体Element角色:需要被增加和特殊处理的原始对象,有基本的属性,并且需要实现 accept方法,通常将自身(this)传给 Visitor 类,让 visitor 类可以操控元素的所有属性,进行额外操作;
- 抽象Visitor接口:定义访问所有element元素的visit方法;
- 具体Visitor角色:实现 visit 方法,对传过来的特定元素进行特定处理;
- 集合类:Object Structure ,持有所有Element元素的集合,提供统一的遍历方法
抽象元素和实现类
public interface Bill {
void accept(AbstractVisitor visitor);
}
@Data
@NoArgsConstructor
@AllArgsConstructor
class BillA implements Bill {
private String name;
@Override
public void accept(AbstractVisitor visitor) {
//将自身作为入参,使得visitor可以访问元素的其他属性
visitor.visit(this);
}
}
@Data
@NoArgsConstructor
@AllArgsConstructor
class BillB implements Bill {
private String name;
@Override
public void accept(AbstractVisitor visitor) {
//将自身作为入参,使得visitor可以访问元素的其他属性
visitor.visit(this);
}
}
抽象访问者和实现类
interface AbstractVisitor {
void visit(BillA billA);
void visit(BillB billB);
}
class VisitorA implements AbstractVisitor {
@Override
public void visit(BillA billA) {
//获取元素的其他属性,做一些特别的处理
System.out.println(billA.getName()+" va for ba");
}
@Override
public void visit(BillB billB) {
//获取元素的其他属性,做一些特别的处理
System.out.println(billB.getName()+" va for bb");
}
}
class VisitorB implements AbstractVisitor {
@Override
public void visit(BillA billA) {
//获取元素的其他属性,做一些特别的处理
System.out.println(billA.getName()+" vb for ba");
}
@Override
public void visit(BillB billB) {
//获取元素的其他属性,做一些特别的处理
System.out.println(billB.getName()+" vb for bb");
}
}
数据集合类 Object Structure
class Structure {
private List<Bill> list;
public void setList(List<Bill> list) {
this.list = list;
}
public void view(AbstractVisitor abstractVisitor) {
list.forEach(bill -> bill.accept(abstractVisitor));
}
}
class Test {
public static void main(String[] args) {
Structure structure = new Structure();
structure.setList(Arrays.asList(new BillA("A1"), new BillB("B1 "),new BillA("A2"),new BillB("B2")));
structure.view(new VisitorA());
System.out.println("-------------------");
structure.view(new VisitorB());
}
}
- 需要对元素进行多种不同类型的操作时可以使用访问者模式,避免污染元素本身的属性(避免在元素内部加很多其他的逻辑)
- 元素本身属性和结构较为稳定,经常需要基于此结构增加额外的处理逻辑
其他案例:
- 如学生实体有年龄、身高、体重、成绩、班级等属性,
- 家庭老师需要根据学生的成绩来制定学习内容;体育老师需要根据学生的身体素质制定合适的训练计划,
- 学生还分为高中生、初中生,各个老师对应的处理逻辑也不一样。
此时就可以使用访问者模式,学生除本身的基本属性外不需要添加额外的逻辑(体育老师,家庭老师等等对课程安排的处理)
再比如:员工分为正式工、实习生、兼职工、外包等等类型,财务发工资的时候有不同的处理。
2)中介者模式
中介者模式:Mediator Pattern
主要角色:
- 抽象中介者:Mediator,定义基本的业务方法
- 实现类中介者:Concrete Mediator,持有被调停者引用
- 抽象被调停者:定义基本的业务方法,不一定有接口
- 实现类被调停者:业务类
@AllArgsConstructor
public abstract class Colleague {
protected Mediator mediator;
public abstract void sendMsg(String msg);
public abstract void notifyMsg(String msg);
}
class ConcreteColleague1 extends Colleague {
public ConcreteColleague1(Mediator mediator) {
super(mediator);
mediator.registerColleague(this);
}
@Override
public void sendMsg(String msg) {
mediator.send(msg, this);
}
@Override
public void notifyMsg(String msg) {
System.out.println("同事1获得消息 " + msg);
}
}
class ConcreteColleague2 extends Colleague {
public ConcreteColleague2(Mediator mediator) {
super(mediator);
mediator.registerColleague(this);
}
@Override
public void sendMsg(String msg) {
mediator.send(msg, this);
}
@Override
public void notifyMsg(String msg) {
System.out.println("同事2获得消息 " + msg);
}
}
interface Mediator {
void send(String message, Colleague colleague);
void registerColleague(Colleague colleague);
}
@Setter
class ConcreteMediator implements Mediator {
ConcreteColleague1 concreteColleague1;
ConcreteColleague2 concreteColleague2;
@Override
public void registerColleague(Colleague colleague){
if(colleague instanceof ConcreteColleague1){
concreteColleague1= (ConcreteColleague1) colleague;
}
if(colleague instanceof ConcreteColleague2){
concreteColleague2= (ConcreteColleague2) colleague;
}
}
@Override
public void send(String message, Colleague colleague) {
if (colleague == concreteColleague1) {
concreteColleague1.notifyMsg(message);
}
if (colleague == concreteColleague2) {
concreteColleague2.notifyMsg(message);
}
}
}
class Test {
public static void main(String[] args) {
ConcreteMediator mediator = new ConcreteMediator();
ConcreteColleague1 c1 = new ConcreteColleague1(mediator);
ConcreteColleague2 c2 = new ConcreteColleague2(mediator);
c1.sendMsg("asd");
c2.sendMsg("nini");
}
}
中介者模式旨在处理类于类之间的依赖关系,避免两个类直接关联。而是添加一个中介者用来处理两个类。
但其实类于类之间的依赖关系并不复杂时,并不需要使用中介者模式。反而会增加复杂度。
避免过于滥用中介者模式。
3)解释器模式
解释器模式:Interpreter Pattern
主要角色:
上下文:Context,存储所有的表达式
终结表达式:实现设计中的解释作用,在本例中为取值
非终结表达式: 实现设计中的运算左右,在本例中为运算规则
public interface Expression {
int interpreter(Context context);
}
abstract class NonTerminalExpression implements Expression {
Expression e1, e2;
public NonTerminalExpression(Expression e1, Expression e2) {
this.e1 = e1;
this.e2 = e2;
}
}
class MinusOperation extends NonTerminalExpression {
public MinusOperation(Expression e1, Expression e2) {
super(e1, e2);
}
@Override
public int interpreter(Context context) {
return this.e1.interpreter(context) - this.e2.interpreter(context);
}
}
class PlusOperation extends NonTerminalExpression {
public PlusOperation(Expression e1, Expression e2) {
super(e1, e2);
}
@Override
public int interpreter(Context context) {
return this.e1.interpreter(context) + this.e2.interpreter(context);
}
}
//终结表达式
class TerminalExpression implements Expression {
String variable;
public TerminalExpression(String variable) {
this.variable = variable;
}
@Override
public int interpreter(Context context) {
//终结表达式的解释左右,此处终结表达式调用lookup,为从Context中取值
return context.lookup(this);
}
}
class Context {
private Map<Expression, Integer> map = new HashMap<>();
public void add(Expression s, Integer value) {
map.put(s, value);
}
public int lookup(Expression s) {
return map.get(s);
}
}
class Test {
public static void main(String[] args) {
Context context = new Context();
//定义基本值
TerminalExpression terminalA = new TerminalExpression("a");
TerminalExpression terminalB = new TerminalExpression("b");
TerminalExpression terminalC = new TerminalExpression("c");
context.add(terminalA, 4);
context.add(terminalB, 8);
context.add(terminalC, 2);
//定义规则
int result = new MinusOperation(
new PlusOperation(terminalA, terminalB), terminalC
).interpreter(context);
System.out.println(result);
}
}