设计模式之策略模式(七)
策略模式就是定义一系列的算法,把它们的一个个封装起来,并且使它们可以相互替换,Strategy模式使算法独立于使用它的客户而变化。
故事中的情节采用旁敲侧击,没有奏效又采取了引蛇出洞,最后只好来单刀直入啦,通过上边的故事,要钱也是一种策略,接下来我们来进入主题,策略模式。
抽象策略角色(Strategy)
通常用一个抽象类或者接口来实现,主要定义这个算法所完成的功能。
具体策略角色(ConcreteStrategy)
包装了相关的算法和行为。
环境角色(Context)
持有策略类的引用。
一种行为模式,对算法封装,使得客户端独立于各个策略,扩展性强,添加策略无非就是添加一个具体的实现类而已,代价非常低。
抽象策略角色
public interface Operation {
public double op(double a, double b);
}
添加4个具体策略角色类Add、Sub、Multi、Div
public class Add implements Operation {
@Override
public double op(double a, double b) {
double result = a + b;
return result;
}
}
public class Sub implements Operation {
@Override
public double op(double a, double b) {
double result = a-b;
return result;
}
}
public class Multi implements Operation {
@Override
public double op(double a, double b) {
double result = a*b;
return result;
}
}
public class Div implements Operation {
@Override
public double op(double a, double b) {
double result = a/b;
return result;
}
}
环境角色
public class Calc {
public static final Add add = new Add();
public static final Sub sub = new Sub();
public static final Div div = new Div();
public static final Multi multi = new Multi();
}
测试类
public class Test {
public static void main (String[] args) {
Calc calc = new Calc();
double add = calc.add.op(11,22);
double sub = calc.sub.op(22,11);
double div = calc.div.op(33,11);
double multi = calc.multi.op(33,33);
System.out.println(add);
System.out.println(sub);
System.out.println(div);
System.out.println(multi);
}
}
测试结果
33.0
11.0
3.0
1089.0
优点
提供管理相关算法的办法,避免使用多重的条件判断语句。扩展性更好,在策略模式中扩展策略实现非常的容易,只要新增一个策略实现类,然后在使用策略实现的地方,使用这个新的策略实现就好了。
缺点
客户端必须知道所有的策略类,自己决定使用哪一个策略类,造成很多的策略类。
假设现在要设计一个贩卖各类书籍的电子商务的购物车(Shopping Cat) 系统。一个最简单的情况就是把所有货品的单价乘上数量,但是实际上肯定比这要复杂。会有哪些情况呢?
可能对所有的儿童类图书实行每本图书实现1元的折扣
计算类图书提供每本7%的促销折扣,而对电子类图书有3%的折扣,其余图书没有折扣
还会有新的折扣策略,由于这样复杂的折扣算法,使得价格计算问题需要系统地解决。
方式一
public abstract class Book {
// 价格
private double price;
// 书名称
private String name;
// 定义计算折扣的变量
public abstract double getSalePrice();
// 省略set()get()方法
}
儿童图书类
public class ChildrenBook extends Book {
public ChildrenBook (String name,double price) {
this.setName(name);
this.setPrice(price);
}
@Override
public double getSalePrice() {
return this.getPrice() -1;
}
}
计算机类
public class CsBook extends Book {
public CsBook (String name,double price) {
this.setName(name);
this.setPrice(price);
}
@Override
public double getSalePrice() {
return this.getPrice() * 0.7;
}
}
测试类
public class Client {
public static void main(String[] args) {
// 方式一 在实现抽象类方法,在子类进行各自实现打折算法,即使打折算法相同。也要重写。
Book book = new CsBook("Think in java",45);
Book childrenBook = new ChildrenBook("安徒生的故事",20);
System.out.println("Think in java原价:"+book.getPrice() +"打折价:"+book.getSalePrice());
System.out.println("安徒生的故事原价:"+childrenBook.getPrice() +"打折价:"+childrenBook.getSalePrice());
}
}
小结:
每个子类必须都各自实现打折算法,即使打折算法相同。
方式二
public abstract class Book {
private double price;
private String name;
// 把打折策略代码提到父类来实现
public static double toSalePrice (Book book) {
double result = 0;
if (book instanceof ChildrenBook) {
result = book.getPrice() - 1;
} else if (book instanceof CsBook) {
result = book.getPrice() * 0.7;
} else {
result = 0;
}
return result;
}
}
测试类
public class Client {
public static void main(String[] args) {
Book book = new CsBook("Think in java",45);
Book childrenBook = new ChildrenBook("安徒生的故事",20);
System.out.println("Think in java原价:"+book.getPrice() +"打折价:"+ Book.toSalePrice(book));
System.out.println("安徒生的故事原价:"+childrenBook.getPrice() +"打折价:"+ Book.toSalePrice(childrenBook));
}
}
小结:
如果策略模式复杂用if判断比较乱,并且策略修改或怎加是需要改变原代码。
策略模式
抽象策略角色
public interface BookInterface {
public double dissCount(double price);
}
具体实现角色(计算机图书类)
public class CalculationBook implements BookInterface {
@Override
public double dissCount(double price) {
double result = price * 0.7;
return result;
}
}
具体实现角色(儿童图书类)
public class ChildrenBook implements BookInterface {
@Override
public double dissCount(double price) {
double result = price - 1;
return result;
}
}
环境角色
public class DiscountStrategy {
public final static CalculationBook book = new CalculationBook();
public final static ChildrenBook childrenBook = new ChildrenBook();
}
在Book类添加折扣策略类
public abstract class Book {
private double price;
private String name;
public abstract double getSalePrice();
// 折扣策略
private DiscountStrategy discountStrategy;
}
测试类
public class Client {
public static void main(String[] args) {
Book book = new CsBook("Think in java",45);
Book childrenBook = new ChildrenBook("安徒生的故事",20);
System.out.println("Think in java原价:"+book.getPrice() +"打折价:"+ book.getDiscountStrategy().book.dissCount(book.getPrice()));
System.out.println("安徒生的故事原价:"+childrenBook.getPrice() +"打折价:"+ book.getDiscountStrategy().childrenBook.dissCount(childrenBook.getPrice()));
}
}
小结:
使用策略模式更加灵活,可以任意增加具体角色类。
适用场景
系统有很多类,而他们区别仅仅在与它们的行为,动态选择几种算法中的一种,一个对象有很多行为。
作用
就是把具体的算法实现从业务逻辑中剥离出来,成为一系列独立算法类,使得它们可以相互替换。
重点
策略模式体现了开闭原则:策略模式把一系列的可变算法进行封装,从而定义了良好的程序结构,在出现新的算法的时候,可以很容易的将新的算法实现加入到已有的系统中,而已有的实现不需要修改。
策略模式体现了里氏替换原则:策略模式是一个扁平的结构,各个策略实现都是兄弟关系,实现了同一个接口或者继承了同一个抽象类。这样只要使用策略的客户端保持面向抽象编程,就可以动态的切换不同的策略实现以进行替换。