设计模式之策略模式(七)

策略模式就是定义一系列的算法,把它们的一个个封装起来,并且使它们可以相互替换,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()));

         }
}

小结:

使用策略模式更加灵活,可以任意增加具体角色类。


适用场景

系统有很多类,而他们区别仅仅在与它们的行为,动态选择几种算法中的一种,一个对象有很多行为。

作用

就是把具体的算法实现从业务逻辑中剥离出来,成为一系列独立算法类,使得它们可以相互替换。

重点

?策略模式体现了开闭原则:策略模式把一系列的可变算法进行封装,从而定义了良好的程序结构,在出现新的算法的时候,可以很容易的将新的算法实现加入到已有的系统中,而已有的实现不需要修改。
?策略模式体现了里氏替换原则:策略模式是一个扁平的结构,各个策略实现都是兄弟关系,实现了同一个接口或者继承了同一个抽象类。这样只要使用策略的客户端保持面向抽象编程,就可以动态的切换不同的策略实现以进行替换。


注意啦! 往期文章目录在这里

最后编辑于
?著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,172评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,346评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,788评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,299评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,409评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,467评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,476评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,262评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,699评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,994评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,167评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,827评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,499评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,149评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,387评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,028评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,055评论 2 352

推荐阅读更多精彩内容