ps:本文系转载文章,阅读原文可获取源码,文章末尾有原文链接

ps:这一篇是写策略模式、方法模板模式和观察者模式

1、策略模式

定义了一系列算法,把每个算法的内部实现细节都隐藏起来,并且它们之间可以相互取代,同时算法的改变不会影响到客户端。

策略模式有以下几种角色:

(1)抽象策略类:定义了一个公共接口,具体策略类以不同的方式实现这个接口。

(2)具体策略类:实现了抽象策略的接口,实现了具体的算法。

(3)环境类:它的属性含有一个抽象策略类的引用,提供给客户端使用。

这里举个例子,假设买某一件商品,原件超过700块钱的就打8折,原价超过850块钱的就打85折,我们用代码实现一下:

(1)抽象策略类,新建一个 Price 接口:

public interface Price {

public double getPrice(double s);

}

(2)具体策略类,新建一个 Price8 类并实现 Price 接口:

public class Price8 implements Price{

@Override
public double getPrice(double s) {

System.out.println("打8折");
return 0.8*s;

}

}

(3)具体策略类,新建一个 Price85 类并实现 Price 接口:

public class Price85 implements Price{

@Override
public double getPrice(double s) {

System.out.println("打85折");
return 0.85*s;

}

}

(4)环境类,新建一个 Context 类:

public class Context {

private Price price;
public Context(Price price) {
  setPrice(price);
}
public void printPrice(double s) {
  System.out.println("原价为:" + s);
  System.out.println("打折后的价格为:" + price.getPrice(s));
}

public void setPrice(Price price) {
  this.price = price;
}

}

客户端进行测试调用:

   Context cxt = new Context(new Price85());
   cxt.printPrice(996);
   
   cxt.setPrice(new Price8());
   cxt.printPrice(800);

日志打印如下所示:

图片

当客户端知道具体的策略规则之后,虽然环境类持有抽象策略类的引用,但是在客户端这里传入具体的策略类对象给环境类同时并把相应的原价传给环境类的 printPrice 方法;原件超过700块钱的就打8折是一个策略,原价超过850块钱的就打85折是另外一个策略。

使用策略模式避免出现多层条件语句,它的新算法灵活,新添加具体策略类的同时,不需要改原代码,只需要把具体策略类对象传递给环境类对象相应的方法;算法的具体实现放在具体策略类中,而算法的调用是放在环境类中,具体策略类和环境类进行了分离;但是客户端需要知道所有具体策略算法的不同,才能更好的选择适合的具体策略类;策略模式制造很多具体的策略类,维护复杂度逐渐增加。

2、方法模板模式

定义一个操作流程中的算法框架,将算法的一些步骤延伸到子类中,让子类不改变该算法结构的条件下再次定义该算法的某些特定步骤。

方法模板模式有以下角色:

(1)抽象类:定义了算法的框架,按照开发者指定顺序调用基本方法。

基本方法包含如下所示:

抽象方法:在抽象类中进行定义,由子类自己去实现。

具体方法:在抽象类中已经实现,由子类中直接使用或重写它。

钩子方法:在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种;比如说有一个接口或者一个抽象类A,这个接口或者抽象类A里有N(N大于1)个方法,我们只想用其中一个方法,我们可以写一个抽象类B实现这个接口或者继承抽象类A,在这个抽象类B里将你要用的那个方法设置为abstract method,其它方法进行空实现,然后我们再写一个子类C继承这个抽象类B,就不需要实现其它不用的方法,只需要实现 abstract method 方法,abstract method 方法就是钩子方法。

(2)具体实现类:实现抽象类中或者接口中所声明的抽象方法和钩子方法,它们是模板方法中逻辑组成部分。

下面我们举个例子,例如,去银行办理业务一般要经过以下3个流程:取号、办理具体业务(取钱、理财等)、对银行工作人员进行评分等;我们用代码模拟一下:

(1)抽象类,写一个抽象类 Father:

public abstract class Father {

public void a(){
  System.out.println("排队取号");
}
public void c() {
  System.out.println("给工作人员评分");
}
public abstract void b();
public void d() {
  this.a();
  this.b();
  this.c();
}

}

(2)具体实现类,写一个 SonA 类并继承 Father:

class SonA extends Father {

@Override
public void b() {

System.out.println("我要取钱");

}

}

(3)具体实现类,写一个 SonB 类并继承 Father:

class SonB extends Father {

@Override
public void b() {

System.out.println("我要理财");

}

}

(4)在客户端进行测试调用:

    Father f = new SonA();
    f.d();
    System.out.println("--------------------");
    Father f2 = new SonB();
    f2.d();

日志打印如下所示:

图片

抽象类 Father 提供了模板方法 d 并把 a、b 和 c 这3个方法按照顺序进行调用,其中 a 和 c 方法已经是固定好了的,而 b 方法是具体的业务,每个 Father 子类具体的业务不一样,所以 b 方法由子类去实现并调用模板方法 d。

虽然方法模板模式封装了抽象类或者接口不变部分方法并提取了公共一些代码,扩展可变部分,把可变部分拓展到子类中去实现;但是对每个不同的业务都要新添加一个子类并继承拥有方法模板的父类,这会导致类的个数增加,设计更加抽象,同时继承的关系也会带来一定的局限性,父类每添加一个抽象方法,所有子类都要添加一个实现方法。

3、观察者模式

多个对象之间存在一对多的依赖关系,当一个被依赖的对象的状态发生改变时,依赖它的所有对象都会被通知并立即更新。

(1)抽象被观察者角色:提供用于保存被观察者对象的聚集类和增加、删除被观察者对象的方法和通知所有被观察者的方法。

(2)具体被观察者角色:它实现抽象被观察者角色的方法,在被观察者内部状态改变时,通知所有注册过的观察者对象。

(3)抽象观察者角色:它是一个抽象类或接口,声明一个当接到具体的更改通知时被调用的方法。

(4)具体观察者角色:实现抽象观察者中定义的抽象方法,该方法接收到通知时更新自身的状态。

下面我们用代码举个具体的例子:

(1)抽象被观察者角色,写一个 ISend 接口:

public interface ISend {
public void register(Reciever reciever);
public void notifiAll(int i);
public void setState(int state);
public int getSate();
}

(2)具体被观察者角色,写一个 Send 类并实现 ISend 接口:

public class Send implements ISend{

private int state = 0;
private List<Reciever> list = new ArrayList<Reciever>();
@Override
public void setState(int state) {
  this.state = state;
}
@Override
public int getSate() {
  return this.state;
}
@Override
public void register(Reciever reciever) {
  list.add(reciever);
}
@Override
public void notifiAll(int i) {
  for (Reciever reciever:list) {
    reciever.update(i);
  }
}

}

(3)抽象观察者角色,写一个 Reciever 接口:

public interface Reciever {

public void update(int i);

}

(4)具体观察者角色,写一个 ReceiverA 类并实现 Reciever 接口:

class ReceiverA implements Reciever {
private int state = 0;
private String nameString;
public ReceiverA(String name) {

this.nameString = name;

}
@Override
public void update(int i) {

state = i;

}
public int getState() {

return this.state;

}
@Override
public String toString() {

// TODO 自动生成的方法存根
return nameString + "----------" + state;

}

}

(5)具体观察者角色,写一个 ReceiverB 类并实现 Reciever 接口:

class ReceiverB implements Reciever {
private int state = 0;
private String nameString;
public ReceiverB(String name) {

this.nameString = name;

}
@Override
public void update(int i) {

state = i;

}
public int getState() {

return this.state;

}
@Override
public String toString() {

// TODO 自动生成的方法存根
return nameString + "----------" + state;

}

}

(6)具体观察者角色,写一个 ReceiverC 类并实现 Reciever 接口:

class ReceiverC implements Reciever {
private int state = 0;
private String nameString;
public ReceiverC(String name) {

this.nameString = name;

}
@Override
public void update(int i) {

state = i;

}
public int getState() {

return this.state;

}
@Override
public String toString() {

// TODO 自动生成的方法存根
return nameString + "----------" + state;

}

}

(7)客户端进行测试调用:

    ISend send = new Send();
    Reciever recieverA = new ReceiverA("A");
    Reciever recieverB = new ReceiverB("B");
    Reciever recieverC = new ReceiverC("C");
    send.register(recieverA);
    send.register(recieverC);
    send.register(recieverB);
    send.setState(999);
    send.notifiAll(send.getSate());
    System.out.println(recieverA);
    System.out.println(recieverB);
    System.out.println(recieverC);

日志打印如下所示:

图片

一开始 ReceiverA、ReceiverB 和 ReceiverC 的属性 state 为0,并对 send 进行注册,这样 send 和 ReceiverA、ReceiverB、ReceiverC 形成一对多的依赖关系,send 的 state 的属性值发生改变时,会通知 ReceiverA、ReceiverB 和 ReceiverC 的 state 属性值发生改变。

观察者模式虽然降低了被观察者与观察者之间的耦合性,建立了一对多的依赖关系;但是它们之间的依赖关系不能完全解除,很有可能出现相互引用的情况。


公众号小二玩编程
4 声望4 粉丝

活到老,学到老。