策略模式(常用)
策略模式:定义一系类算法,将每一个算法封装起来,并让他们可以相互替换,策略模式让算法可以独立于使用它的客户而变化。
1.代码案例
某软件公司为某电影院开发了一套影院售票系统,在该系统中需要为不同类型的用户提供不同的电影票打折方式,具体打折方案如下。 (1)学生凭学生证可享受票价8折优惠。 (2)年龄在10周岁及以下的儿童可享受每张票减免10 元的优惠(原始票价需大于等于20元) (3)影院VIP 用户除享受票价半价优惠外还可进行积分,积分累计到一定额度可换取电影院赠送的礼品。 该系统在将来可能还要根据需要引入新的打折方式。试使用策略模式设计该影院售票系统的打折方案。
MovieTicket 电影票类,充当环境类
package com.company.Demo;
public class MovieTicket {
private double price;
private Discount discount;
public double getPrice() {
//调用折扣类的折扣计算方法
return discount.calculate(this.price);
}
public void setPrice(double price) {
this.price = price;
}
// 注入一个折扣类对象
public void setDiscount(Discount discount) {
this.discount = discount;
}
}
Discount 折扣类,充当抽象策略类
package com.company.Demo;
public interface Discount {
double calculate(double price);
}
StudentDiscount 学生折扣类,充当具体折扣类
package com.company.Demo;
public class StudentDiscount implements Discount{
@Override
public double calculate(double price) {
System.out.print("学生票: ");
return price*0.8;
}
}
VIPDiscount VIP折扣类,充当具体折扣类
package com.company.Demo;
public class VIPDiscount implements Discount{
@Override
public double calculate(double price) {
System.out.println("VIP票:");
System.out.println("赠送积分");
return price*0.5;
}
}
ChildrenDiscount 儿童折扣类,充当具体折扣类
package com.company.Demo;
public class ChildrenDiscount implements Discount{
@Override
public double calculate(double price) {
System.out.println("儿童票: ");
if (price>=20){
return price-10;
}
return price;
}
}
配置文件config.xml 在配置文件中存储了具体命令类的类名
<?xml version="1.0" encoding="ISO-8859-1"?>
<config>
<className>com.company.Demo.StudentDiscount</className>
</config>
XMLUtil工具类,解析config.xml文件获得具体类的对象
package com.company.Demo;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
public class XMLUtil {
//使用此方法 用于从xml 配置文件中提取具体类的类名,并且返回一个对象
public static Object getBean(){
// 创建 DOM 文档对象
DocumentBuilderFactory builderFactory=DocumentBuilderFactory.newInstance();
try {
DocumentBuilder builder = builderFactory.newDocumentBuilder();
Document parse = builder.parse(new File("D:\\javaProject\\MoreThread\\src\\config.xml"));
// 获取包名的文本节点
NodeList nodeList = parse.getElementsByTagName("className");
Node node = nodeList.item(0).getFirstChild();
//通过类名反射实例化对象,并且返回
String nodeName = node.getNodeValue();
Class<?> clazz = Class.forName(nodeName);
return clazz.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
测试;
package com.company.Demo;
public class Main {
public static void main(String[] args) {
MovieTicket movieTicket=new MovieTicket();
Discount bean =(Discount) XMLUtil.getBean();
movieTicket.setDiscount(bean);
movieTicket.setPrice(60.0);
System.out.println(movieTicket.getPrice());
}
}
结果:
学生票: 48.0
2.策略模式的优点
1.策略模式提供了对开闭原则的完美支持,用户可以在不修改原有系统代码的基础上选择算法或行为,也可以灵活地对新增加的算法或行为
2.策略模式提供了管理相关算法族的办法,策略类的等级结构定义了一个算法或行为族,恰当地使用继承可以把公共的代码移到抽象策略类中,从而避免重复代码
3.策略模式提供了一种可以替换继承关系的办法,如果不使用策略模式,那么使用算法的环境类就可能会有一些子类,每一个子类提供一种不同的算法,但是这样一来算法的使用就和算法本身混在一起,不符合单一职责原则,决定定使用哪一种算法的逻辑和该算法本身混合在一起,从而不可能再独立通化;而且便用继承无法实现算法或行为在程厅运行时的动态切换。
4.策略模式提供了一种算法的复用机制,由于将算法单独提取出来封装在策略类中,因此不同的环境类可以方便地复用这些策略类。
5.使用策略模式可以避免多重条件选择语句。多重条件选择语句不易维护,它把采取哪一种算法或行为的逻辑与算法或行为本身的实现逻辑混合在一起,将它们全部硬编码在一个庞大的多重条件选择语句中,比直接继承环境类的办法还要原始和落后。
3.策略模式的缺点
1.客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户路必须理解这些算法的区别,以便适时选择怡当的算法。换而言之,策路模式只适用于客户瑞知道所有算法或行为的情况。
2.策略模式将造成系统产生很多具体策略类,任何细小的变化都将导致系统要增加个新的具体策略类。
3.无法同时在客户端使用多个策略类,也就是说,在使用策略模式时客户端每次只能使用一个策略类,不支持使用一个策略类完成部分功能后再使用另一个策略类完成剩余功能的情况。
4.策略模式的场景
(1)一个系统需要动态地在几种算法中选择一种,那么可以将这些算法封装到一个个的具体算法类中,而这些具体算法类都是一个抽象算法类的子类。换而言之,这些具体算法,换而言之,这些具体算法类均有统的接口,根据里氏代换原则和面向对象的多态性,客户端可以选择使用任何一个具体算法类,并只需要维持一数据类型是抽象算法类的对象
(2)一个对象有很多行为,如果不用怡当的模式,这些行为则只好便用多重条件选择语句来实现。此时使用策略模式把这些行为转移到相应的具体策略类里面,就可以避免使用难以维护的多重条件选择语句。
(3)不希望客户端知道复杂的、与算法相关的数据结构,在具体策略类中封装算法与相关的数据结构,可以提高算法的保密性与安全性
参考:《JAVA 设计模式》