![](https://i-blog.csdnimg.cn/blog_migrate/bbb5da7b1dc01452a2abf691e32b0622.jpeg)
我是荔园微风,作为一名在IT界整整25年的老兵,今天总结一下Windows环境下如何编程实现策略模式(设计模式)。
不知道大家有没有这样的感觉,看了一大堆编程和设计模式的书,却还是很难理解设计模式,无从下手。为什么?因为你看的都是理论书籍。
我今天就在Windows操作系统上安装好JAVA的IDE编程工具,并用JAVA语言来实现一个策略模式,真实的实现一个,你看懂代码后,自然就明白了。
策略模式Strategy Pattern(行为型设计模式)
定义:定义一系列算法,将每一个算法封装起来,并让它们可以相互替换。策略模式让算法可以独立于使用它的客户而变化。
上面定义听懂了吗?莫名其妙看不懂对吧。所以我们还是来看看实现生活中的例子。
比如我们要让机器人通过地图路径计算到达一个目的地,从这里到目的地有很多不同的路径算法,这种设计模式可以把各个算法封装好,然后让算法自由切换和扩展。策略模式对应于解决某一问题的一个算法集,允许机器人从该算法集中任选一个算法解决某一问题,同时可以方便地更换算法或者增加新的算法。策略模式实现了算法定义和算法使用的分离,它通过继承和多态的机制实现对算法集的使用和管理,是一个简单、实用的设计模式。
![](https://i-blog.csdnimg.cn/blog_migrate/34f82ec68408184eb2b3609b354fdd4a.jpeg)
在一般情况下,机器人到达某个目标的途径不止一条,可根据实际情况(目的地距离、每条路径的权重、运行时间等)来选择一种最适合的出行方式。在软件开发中也常常会遇到类似的情况,实现某一个功能(例如排序、查找等)有多种算法,一种常用的方法是将所有的算法集中在一个类中,在该类中提供多个方法,每一个方法对应一个具体的算法;当然也可以将这些算法封装在一个方法中,通过条件判断语句进行选择。这样做虽然可以,但如果需要增加一种新的算法,需要修改算法类的代码;更换算法也需要修改客户端调用代码。在这个统一的算法类中封装了大量算法,代码非常复杂,维护也很困难。
可以使用策略模式来解决上面问题。在策略模式中可以定义一些独立的类来封装不同的算法,每一个类封装一种具体的算法,在这里每一个封装算法的类都可以称为一种策略(Strategy),为了保证这些策略在使用时具有一致性,一般会提供一个抽象的策略类来做算法的声明,而每种算法对应于一个具体策略类。
![](https://i-blog.csdnimg.cn/blog_migrate/4f681ad9cc7d08324a1d440ddeb54b05.png)
由上图策略模式包含Context(环境类)、Strategy(抽象策略类)、ConcreteStrategy(具体策略类)3个角色。环境类是使用算法的角色,它在解决某个问题时可以采用多种策略。在环境类中维持一个对抽象策略类的引用实例,用于定义所采用的策略。抽象策略类为所支持的算法声明了抽象方法,是所有策略类的父类,它可以是抽象类或具体类,也可以是接口。环境类通过抽象策略类中声明的方法在运行时调用具体策略类中实现的算法。具体策略类实现了在抽象策略类中声明的算法,在运行时具体策略类将覆盖在环境类中定义的抽象策略类对象,使用一种具体的算法实现某个业务功能。
JAVA代码实现
public abstract class AbstractStrategy {
public abstract void algorithm(); //声明抽象算法
}
public class ConcreteStrategyA extends AbstractStrategy {
//算法的具体实现
public void algorithm(){
//算法A
}
}
public class Context{
private AbstractStrategy strategy; //维持一个对抽象策略类的引用
public void setStrategy(Abstractstrategy strategy){
this.strategy=strategy;
}
//调用策略类中的算法
public void algorithm()(
strategy.algorithm();
}
}
public class Client{
public static void main(String args[]){
Context context = new Context();
AbstractStrategy strategy;
//可在运行时指定类型,通过配置文件和反射机制实现
strategy = new ConcreteStrategyA();
context. setStrategy(strategy);
context.algorithm();
}
策略模式是对算法的封装,它把算法的责任和算法本身分开,委派给不同的对象管理。策略模式通常把一个系列的算法封装到一系列具体策略类里面,作为抽象策略类的子类。在策略模式中对环境类和抽象策略类的理解非常重要,环境类是需要使用算法的类。在一个系统中可以存在多个环境类,它们可能需要重用一些相同的算法。
应用实例
![](https://i-blog.csdnimg.cn/blog_migrate/8ee5fdac173d8451dbd61b87492b8cb0.png)
我们一起来看一个实例,某大型超市为吸引顾客,开发了一套会员卡打折系统,在该系统中需要为不同类型的客户提供不同的商品打折方式,具体打折方案如下:
当地老年人凭身份证可享受商品6折优惠。年龄在10周岁及以下的小朋友可享受每件商品减免10元的优惠(原价需大于10元,不然不够减)。老顾客凭会员卡除享受5折优惠外还可进行积分,积分累计到一定额度可换取赠送礼品。该系统在将来可能还要根据需要引入新的打折方式。
(1)Supermarket:顾客价格类,充当环境类。
package designpatterns. strategy;
public class Supermarket {
private double price;
private Discount discount;/维持一个对抽象折扣类的引用
public void setPrice(double price) {
this.price = price;
}
//注入一个折扣类对象
public void setDiscount(Discount discount) {
this.discount = discount;
}
public double getPrice() {
//调用折扣类的折扣价计算方法
return discount.calculate(this.price);
}
}
(2)Discount:折扣类,充当抽象策略类。
package designpatterns. strategy;
public interface Discount{
public double calculate(double price);
}
(3)OldpeopleDiscount:老年人折扣类,充当具体策略类。
package designpatterns. strategy;
public class OldpeopleDiscount implements Discount{
private final double DISCOUNT=0.6;
public double calculate(double price){
System.out.println("老年人 ");
return price*DISCOUNT;
}
(4)ChildrenDiscount:小朋友折扣类,充当具体策略类。
package designpatterns. strategy;
public class ChildrenDiscount implements Discount {
private final double DISCOUNT= 10;
public double calculate(double price){
System.out.println("小朋友");
if(price>10){
return price-DISCOUNT;
else {
return price;
}
}
}
(5)VIPDiscount:老顾客折扣类,充当具体策略类。
package designpatterns. strategy;
public class VIPDiscount implements Discount{
private final double DISCOUNT=0.5;
public double calculate(double price){
System.out.println("VIP");
System.out.println("积分+1");
return price*DISCOUNT;
}
}
(6)配置文件config.xml,在配置文件中存储了具体折扣类的类名
<?xml version="1.0"?>
<config>
<className> designpatterns. strategy.OldPeopleDiscount </className>
</config>
(7)XMLUtil:工具类。
package designpatterns. strategy,
import javax.xml.parsers.*;
import org.3c.dom.*;
import java.io.*;
public class XMLUtil {
//该方法用于从XML配置文件中提取具体类的类名,并返回一个实例对象
public static Object getBean()(
try {
//创建DOM文档对象
DocumentBuilderFactory dFactory= DocumentBuilderFactory.newInstance();
DocumentBuilder builder= dFactory.newDocumentBuilder();
Document doc;
doc= builder.parse(new File("src//designpatterns/strategy/config.xml");
//获取包含类名的文本结点
NodeList nl = doc.getElementsByTagName("className");
Node classNode= nl. item(0).getFirstChild();
String cName= classNode.getNodeValue();
//通过类名生成实例对象并将其返回
Class c=Class.forName(cName);
Object obj=c.newInstance();
return obj;
}
catch(Exception e){
e.printStackTrace();
return null;
}
}
}
(8)Client:客户端测试类。
package designpatterns. strategy;
public class Client (
public static void main(String args[])(
Supermarket su= new Supermarket();
double originalPrice=60.0;
double currentprice;
su.setPrice(originalPrice);
System.out.println("原价为:" + originalPrice);
Discount discount;
discount =(Discount)XMLUtil.getBean(); //读取配置文件并反射生成具体折扣对象
su. setDiscount(discount); //注入折扣对象
currentPrice=su.getPrice();
System.out.println("折扣结果:" + currentPrice);
}
}
输出结果如下:
原价为:60.0
老年人
折扣结果:36.0
用本人自创的画图法来表示函数执行的关系如下:
![](https://i-blog.csdnimg.cn/blog_migrate/1de0d6b5aba76a6d9a11ba46ab8514af.jpeg)
如果需要增加新的打折方式,原有代码均无须修改,只要增加一个新的折扣类作为抽象折扣类的子类,实现在抽象折扣类中声明的打折方法,然后修改配置文件,将原有具体折扣类的类名改为新增折扣类的类名即可,完全符合开闭原则。
各位小伙伴,这次我们就说到这里,下次我们再深入研究windows环境下的各类设计模式实现。
作者简介:荔园微风,1981年生,高级工程师,浙大工学硕士,软件工程项目主管,做过程序员、软件设计师、系统架构师,早期的Windows程序员,Visual Studio忠实用户,C/C++使用者,是一位在计算机界学习、拼搏、奋斗了25年的老将,经历了UNIX时代、桌面WIN32时代、Web应用时代、云计算时代、手机安卓时代、大数据时代、ICT时代、AI深度学习时代、智能机器时代,我不知道未来还会有什么时代,只记得这一路走来,充满着艰辛与收获,愿同大家一起走下去,充满希望的走下去。