设计模式--策略模式
策略模式
定义:定义了算法族,分别将他们封装起来,让它们之间能相互替换。此模式让算法独立于使用算法的用户。
类图
下面我们用两个列子来说明策略模式的实际用途。
1. 从上班开始
有一个程序员类,他有一个名称,然后有一个上班的方法:
代码如下:
//程序员类
public class Programmer {
//姓名
private String name;
//getter & setter
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
// 构造方法
public Programmer(String name) {
this.name = name;
}
//上班的方法
public void GoToWork(){
System.out.println("坐地铁上班!");
}
}
如果,我们的同事上班方式有所不同,有的人坐地铁,有的人做公交车,有的人骑自行车…那么这个类明显不能满足我们的要求,怎么办?有的人想到了继承,就是创造一个坐公交的程序员,让那些坐公交的人继承程序员类,然后重写GoToWork()方法,这个方法是解决了问题,可是我们想想,如果程序员有任何变化,增加或者修改某个属性或者方法,那么就得对子类进行修改,这是一个不能接受的结果?有更优雅的方式吗?有的人想到了接口,就是将上班抽象成接口,然后让不同上班方式去继承接口,再让他们继承程序员类,并且去实现上班接口
这样是将上班行为分离出来了,但是如果增加了步行上班的人、开车上班的人…..等等,代码越来越多了,而且总是要去继承程序员类,实现上班接口,而且有一天,哪个坐地铁的人买了车,改为开车上班呢?是不是的去改代码?没法动态改变?还有没有更优越的方式呢?
是的,用策略模式~!就是将上班的方式改为一个接口,并且以组合的方式放在程序员的内部,然后让各种上班方式去实现这个上班的接口,程序员上班时,动态传入上班的工具,调用接口中的上班方法就可以了。程序员类不需要知道通过什么方式上班,上班的人自己确定通过哪种方式上班就行了:
//上班接口
public interface GoToWork {
void goToWork(String name);
}
//坐地铁上班
public class SubwayToWork implements GoToWork {
// 坐地铁上班
public void goToWork(String name) {
System.out.println(name + "坐地铁上班");
}
}
//坐公交上班
public class BusToWork implements GoToWork {
public void goToWork(String name) {
System.out.println(name + "坐公交上班");
}
}
//开车上班
public class DriveToWork implements GoToWork {
public void goToWork(String name) {
System.out.println(name + "开车上班");
}
}
//程序员类
public class Programmer {
//姓名
private String name;
//将上班方法变成一个接口
private GoToWork goToWork;
// 构造方法
public Programmer(GoToWork goToWork) {
this.goToWork = goToWork;
}
//上班的方法
public void goToWork(String name){
goToWork.goToWork(name);
}
//getter & setter
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public GoToWork getGoToWork() {
return goToWork;
}
public void setGoToWork(GoToWork goToWork) {
this.goToWork = goToWork;
}
}
//测试程序员上班
public class ProgrammerTest {
public static void main(String[] args) {
//坐地铁上班方式上班
SubwayToWork subway = new SubwayToWork();
Programmer p = new Programmer(subway);
p.goToWork("小明");
// 坐公交上班方式上班
BusToWork bus = new BusToWork();
p.setGoToWork(bus);
p.goToWork("小红");
// 有一天小明买车了
DriveToWork drive = new DriveToWork();
p.setGoToWork(drive);
p.goToWork("小明");
}
}
如果哪天增加了一个骑自行车上班的人,那么,只需要去继承GoToWork接口,实现goToWork方法,就行了,不需要改动程序员类,程序员类也不用知道有没有骑自行车上班这个方法。充分解耦了程序员类与上班方式之间的关系。
//结账类
public class CheckOut {
private Commodity commodity; //商品
private String discount; //折扣方式
//构造方法
public CheckOut(Commodity commodity,String discount) {
this.commodity = commodity;
this.discount = discount;
}
//结账方法
public double checkOut(){
if(this.discount.equals("打八折")){
return this.commodity.total() * 0.8;
}else if(this.discount.equals("打五折")){
return this.commodity.total() * 0.5;
}else if(this.discount.equals("满500减200")){
int i = (int)this.commodity.total()/500;
return (this.commodity.total() - (i * 200));
}else{
return 0;
}
}
//getter & setter
public Commodity getCommodity() {
return commodity;
}
public void setCommodity(Commodity commodity) {
this.commodity = commodity;
}
public String getDiscount() {
return discount;
}
public void setDiscount(String discount) {
this.discount = discount;
}
}
//商品类
public class Commodity {
//价格
private double price;
//数量
private double quantity;
//总价
public double total(){
return this.price * this.quantity;
}
//getter & setter
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public double getQuantity() {
return quantity;
}
public void setQuantity(double quantity) {
this.quantity = quantity;
}
}
//测试结账
public class CheckOutTest {
public static void main(String[] args) {
// 商品类
Commodity c = new Commodity();
c.setPrice(500);
c.setQuantity(3);
// 根据购买的商品进行结账
CheckOut checkOut = new CheckOut(c, "满500减200");
System.out.println("您需要付:"+ checkOut.checkOut());
}
}
看到这段代码,然后想想之前的代码,根据设计的原则,也该将结账类中的checkOut结账打折方法独立出来,这是变化的部分,结账也该和打折分开。我们收款时,只需要传入相关的策略,就能得到对应的结果。
我们先看看类图
//结账类
public class CheckOut {
//收银接口
private MakeCollection makeCollection;
//Commodity
private Commodity commodity;
//构造方法
public CheckOut(MakeCollection makeCollection,Commodity commodity) {
this.makeCollection = makeCollection;
this.commodity = commodity;
}
//结账方法
public double makeCollection(){
return makeCollection.makecollection(commodity);
}
//getter & setter
public Commodity getCommodity() {
return commodity;
}
public void setCommodity(Commodity commodity) {
this.commodity = commodity;
}
public MakeCollection getMakeCollection() {
return makeCollection;
}
public void setMakeCollection(MakeCollection makeCollection) {
this.makeCollection = makeCollection;
}
//打折策略接口
public interface MakeCollection {
//根据打折类型收款
double makecollection(Commodity commodity);
}
//默认不打折
public class Default implements MakeCollection {
public double makecollection(Commodity commodity) {
return commodity.total();
}
}
//八折策略
public class Discount8 implements MakeCollection {
public double makecollection(Commodity commodity) {
return commodity.total() * 0.8;
}
}
//打五折策略
public class Half implements MakeCollection {
//打五折
public double makecollection(Commodity commodity) {
return commodity.total() * 0.5;
}
}
//满500减200策略
public class Full500by200 implements MakeCollection {
public double makecollection(Commodity commodity) {
int i = (int)commodity.total() / 500;
return (commodity.total() - (200 * i));
}
}
//商品类
public class Commodity {
//价格
private double price;
//数量
private double quantity;
//总价
public double total(){
return this.price * this.quantity;
}
/**
* 构造方法
* @param price 价格
* @param quantity 数量
*/
public Commodity(double price, double quantity) {
this.price = price;
this.quantity = quantity;
}
//getter & setter
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public double getQuantity() {
return quantity;
}
public void setQuantity(double quantity) {
this.quantity = quantity;
}
}
//收款测试类
public class CheckOutTest {
public static void main(String[] args) {
// 商品类
Commodity c = new Commodity(500,3);
// 选择默认策略
CheckOut checkOut = new CheckOut(new Default(),c);
System.out.println("默认时,您需要付:"+ checkOut.makeCollection());
// 选择满500减200策略
checkOut.setMakeCollection(new Full500by200());
System.out.println("满500减200时,您需要付:"+ checkOut.makeCollection());
//选择打八折策略
checkOut.setMakeCollection(new Discount8());
System.out.println("打八折时,您需要付:"+ checkOut.makeCollection());
// 选择打对折策略
checkOut.setMakeCollection(new Half());
System.out.println("打对折时,您需要付:"+ checkOut.makeCollection());
}
}
//运行结果
默认时,您需要付:1500.0
满500减200时,您需要付:900.0
打八折时,您需要付:1200.0
打对折时,您需要付:750.0
策略模式将变化的方法(算法)通过一个接口进行抽象,然后在使用者中定义了这个接口,并在某个方法(算法)中调用了这个接口的方法(算法),处理或者返回结果。完成了算法在使用者内部解耦的作用。而使用者要更换方法(算法)的时候,可以通过setter方法进行灵活的更换。
策略模式就像我们游戏中的技能,就拿Dota来说,一个角色一般拥有四个技能,当我们技能能够使用的时候(级别满了,当然,CD时间也要有),我们可以灵活的使用技能,技能之间能互换,只是你在按某个技能键的时候,系统自动帮你调用了对应的招式而已。如果你是矮人狙击手,那么,当你发送散弹的时候,就是在调用你具体的技能在地图的某个区域对附近的人进行减血。技能的使用就类似角色使用策略模式来完成操作的。
以上是我个人对策略模式的理解以及总结,拿出来只是为了共同勉励。如果有错误的地方欢迎指正。
参考资料:
Head first 设计模式 (美)弗里曼(Freeman,E.)
大话设计模式 程杰
上一篇: 微信支付接口测试你还没掌握吗?
下一篇: Oracle数据库管理员的职责