策略模式适用于什么样子的场景呢?

    当我们的代码中出现了一连串的if…else…或者是switch…case…语句时,我们的代码体就会很长很臃肿,阅读性大大下降,此时可采用策略模式进行重构。

原理

    策略模式利用的是面向对象语言的三个特性,尤其是继承和多态。首先让多个类继承同一个抽象的父类或者是实现一个接口,在抽象类或者接口中声明我们要实现的抽象方法,在子类或者实现类中写出不同的方法实现。根据多态性,创建一个父类类型的引用,让其指向子类的对象,让根据对象的不同,执行各个同名但不同体的方法。

示例讲解

    以一个在线租赁商店demo为例:

    影片类,具有标题,价格码(对应的是影片的类型):

public class Movie {public static final int CHILDRENS = 2;public static final int REGULAR = 0;public static final int NEW_RELEASE = 1;private String title;private int priceCode;public Movie(String title, int priceCode) {this.title = title;this.priceCode = priceCode;}public int getPriceCode() {return priceCode;}public void setPriceCode(int priceCode) {this.priceCode = priceCode;}public String getTitle() {return title;}
}

    租赁单类,包含一部电影和租赁的日期,同时还包含一个计算租赁金的方法:

public class Rental {private Movie movie;private int dayRented;public Rental(Movie movie, int dayRented) {this.movie = movie;this.dayRented = dayRented;}public Movie getMovie() {return movie;}public int getDayRented() {return dayRented;}public double getAmount(){double thisAmount = 0d;switch (movie.getPriceCode()) {case Movie.REGULAR:thisAmount += 2;if (this.getDayRented() > 2) {thisAmount += (this.getDayRented() - 2) * 1.5;}break;case Movie.NEW_RELEASE:thisAmount += this.getDayRented() * 3;break;case Movie.CHILDRENS:thisAmount += 1.5;if (this.getDayRented() > 3) {thisAmount += (this.getDayRented() - 3) * 1.5;}break;}return thisAmount;}
}

    顾客类,包含姓名、租赁的影片、总的租金以及积分点,和一个打印所有租赁信息的方法printStatement

public class Customer {private String name;double totalAmount;int frequentRenterPoints;private List<Rental> rentals = new ArrayList<>();public Customer(String name) {this.name = name;}public void addRental(Rental arg) {rentals.add(arg);}public String getName() {return name;}public void printHeader(){String header = "Rental Record for " + getName() + "\n";System.out.print(header);}public void printBody(){String body = "";for (Rental each : this.rentals) {frequentRenterPoints++;if ((each.getMovie().getPriceCode() == Movie.NEW_RELEASE) && each.getDayRented() > 1) {frequentRenterPoints++;}body += "\t" + each.getMovie().getTitle() + "\t" + each.getAmount() + "\n";totalAmount += each.getAmount();}System.out.print(body);}public void printTail(){String tail = "";tail += "Amount owed is " + totalAmount + "\n";tail += "You earned " + frequentRenterPoints + " frequent renter points";System.out.print(tail);}public void printStatement(){printHeader();printBody();printTail();}
}

    主类,用于运行和测试:

public class Main {public static void main(String[] args) {Movie movie = new Movie("泰坦尼克号",Movie.REGULAR);Rental rental = new Rental(movie,3);Customer customer = new Customer("Lily");customer.addRental(rental);customer.printStatement();}
}

    运行结果:

Rental Record for Lily泰坦尼克号  3.5
Amount owed is 3.5
You earned 1 frequent renter points

    代码其实不难理解(因为我觉得方法命名得还不错…),根据用户租赁的影片打印出用户消费的票据,每部影片的租金根据电影的标签和租赁时长计算,还可以累积积分点。

    一眼看过去其实还是有几个比较明显的bad code smell,我们暂且先选择性忽视它们…

    Rental类的getAmount()方法属于典型的长方法,又臭又长,大部分的原因是因为这个switch语句:

switch (movie.getPriceCode()) {case Movie.REGULAR:thisAmount += 2;if (this.getDayRented() > 2) {thisAmount += (this.getDayRented() - 2) * 1.5;}break;case Movie.NEW_RELEASE:thisAmount += this.getDayRented() * 3;break;case Movie.CHILDRENS:thisAmount += 1.5;if (this.getDayRented() > 3) {thisAmount += (this.getDayRented() - 3) * 1.5;}break;}

    接下来我们尝试消除这段长代码!!!

    根据策略模式的思想,我们可以让三个不同的类去继承Rental类,在Rental类中添加一个抽象方法,让各个子类去给出不同的实现,这样就可以取代不同的case。

abstract public class Rental {private Movie movie;private int dayRented;public Rental(Movie movie, int dayRented) {this.movie = movie;this.dayRented = dayRented;}public Movie getMovie() {return movie;}public int getDayRented() {return dayRented;}public double getAmount(){double thisAmount = 0d;return countThisAmount(thisAmount);}abstract public double countThisAmount(double thisAmount);
}

    之前的判断逻辑,全都被放到子类的继承的抽象方法去实现:

public class NewReleaseRental extends Rental {public NewReleaseRental(Movie movie, int dayRented) {super(movie, dayRented);}@Overridepublic double countThisAmount(double thisAmount) {thisAmount += this.getDayRented() * 3;return thisAmount;}
}
public class RegularRental extends Rental {public RegularRental(Movie movie, int dayRented) {super(movie, dayRented);}@Overridepublic double countThisAmount(double thisAmount) {thisAmount += 2;if (this.getDayRented() > 2) {thisAmount += (this.getDayRented() - 2) * 1.5;}return thisAmount;}
}
public class ChildrenRental extends Rental{public ChildrenRental(Movie movie, int dayRented) {super(movie, dayRented);}@Overridepublic double countThisAmount(double thisAmount) {thisAmount += 1.5;if (this.getDayRented() > 3) {thisAmount += (this.getDayRented() - 3) * 1.5;}return thisAmount;}
}

    而原来的长方法,变得非常简洁,大大增强了可读性。

public double getAmount(){double thisAmount = 0d;return countThisAmount(thisAmount);}

适用场景

    策略模式正如其名字一样,提供不同的策略,用户只需要决定用哪个算法,算法内部被系统封装。因此,策略模式多用在算法决策系统中,对不同的输入做不同处理:

  • 以不同的格式保存文件;

  • 以不同的算法压缩文件;

  • 以不同的算法截获图象;

总结

    策略模式在重构代码中帮助很大,往往能消除长方法,大大提高代码的可读性。同时,经过策略模式重构后的代码更易于扩展,一旦有新的条件出现,直接构建一个新的策略就可以,不会影响其他代码。

关于重构代码的思考

    策略模式、模板模式、提取方法等都可以去重构代码,好处在于,更易于后期的维护和继续开发,利于代码重用,代码更具有可读性。但是重构也会带来一些问题,频繁的函数调用会更耗费内存,过度地重构浪费开发时间,说不定还会减少可读性。何解呢?业务需求必定是其一(emmmmmm),还是见仁见智吧,每个人都有自己的理解和行为习惯,只要你觉得ok,你的partners也ok,就ok(o)/~

小唐说设计模式————策略模式篇相关推荐

  1. 小唐说设计模式————原型模式

    什么是原型模式 原型模式(Prototype Pattern)是五种创建型模式的其中一种,用原型实例指定创建对象的种类作为原型,并且通过拷贝原型来创建新的对象. 为什么要使用原型模式 新建一个对象有时 ...

  2. JavaScript设计模式系列—模式篇总结(上)

    转载请注明预见才能遇见的博客:http://my.csdn.net/ 原文地址:https://blog.csdn.net/pcaxb/article/details/102517956 JavaSc ...

  3. Python设计模式-策略模式

    Python设计模式-策略模式 代码基于3.5.2,代码如下; #coding:utf-8 #策略模式class sendInterface():def send(self,value):raise ...

  4. 关于设计模式——策略模式-Strategy Pattern

    文章目录 1 策略模式 1.1 模拟鸭子 1.2 设计原则 1.3 整合行为 1.4 模拟鸭子代码的代码 1.5 动态设定行为 1.6 重新查看整体 1.7 继承和组合 1.8 总结 1.9 优劣期间 ...

  5. [设计模式] ------ 策略模式

    策略模式 它定义了算法家族,分别封装起来,让他们直接可以互相替换,此模式让算法的变化,不会影响到使用算法的客户 其实很简单,可能很多人都用到了,只不过还不知道这就是策略模式而已. 比如定义一个接口A, ...

  6. java 策略模式 促销_java设计模式——策略模式

    一. 定义与类型 定义:针对一组算法,将每一种算法都封装到具有共同接口的独立的类中,从而是它们可以相互替换.策略模式的最大特点是使得算法可以在不影响客户端的情况下发生变化,从而改变不同的功能.当代码中 ...

  7. Springboot 使用设计模式- 策略模式

    前言 直白点,什么场景我们需要使用到设计模式- 策略模式. 在平常的springboot项目里面做CRUD,我们的习惯性基本是 一个mapper,一个service,一个serviceImpl. 但是 ...

  8. 李建忠设计模式——策略模式Strategy

    目录 1.策略模式定义 1.动机 2.模式定义 3.结构 2.实现例子 1.问题描述 2.代码实现 3.要点总结 4.参考 1.策略模式定义 1.动机 软件构建过程中,某些对象使用的算法可能多种多样, ...

  9. 15. 星际争霸之php设计模式--策略模式

    题记 ============================================================================== 本php设计模式专辑来源于博客(jy ...

最新文章

  1. mysql 账户管理_如何用MySQL 命令来实现账户管理
  2. Spring Boot 2.3 中开启Spring Security
  3. Web.config 灵活配置
  4. main函数第3个参数envp装的是什么(envp:环境变量)
  5. 如何使用.NET配置文件(一)
  6. 移动时代的互联网格局变化
  7. 软件测试2019:第五次作业—— 安全测试(含安全测试工具实验)
  8. 李洪强漫谈iOS开发[C语言-038]-if else if语句
  9. python运行方法_对python中执行DOS命令的3种方法总结
  10. sql 不同数据库同步数据_什么是SQL数据同步
  11. tcp压力测试工具_DNS压力测试工具之——DNSperf
  12. 15.explain
  13. python参考手册文字版_Python3.8标准库参考手册 中文完整pdf高清版
  14. python是开源的是什么意思_开源是啥意思
  15. 写给区块链初创者的一封信
  16. 响应式网页设计_响应式网页设计中的常用技术
  17. html是用cdn资源,网站常用前端公共库CDN服务资源(百度+360)
  18. 林业调查规划设计单位资质办理认定
  19. 30岁开始学编程,学什么语言比较好?
  20. 「云安全」 什么是云访问安全代理(CASB )?

热门文章

  1. 多媒体应用开发-控制摄像头拍照
  2. 清除idea JetbrainsAgent插件
  3. mac vscode 背景半透明_vscode 酷炫毛玻璃效果 (超详细)
  4. python制作小程序商城_python 实现(简单的一个购物商城小程序)
  5. 农业银行网上银行服务器未响应,win7系统下使用农行k宝没反应如何解决
  6. android_app开发集成mob短信验证码功能
  7. ACM 常用几何基本数学公式
  8. mysql 树莓派3 性能_树莓派 3b 的性能怎么样?
  9. 免费8Uftp上传工具使用图解
  10. 学计算机u盘多少g合适,U盘装系统需要准备多大的U盘才够用呢?