福州一站式品牌推广运营公司,大数据营销试卷,厦门网站的关键词自动排名,建筑网站建设公司文章目录 案例引入传统方案实现实现分析 介绍基本介绍登场角色 案例实现案例一类图实现 案例二类图实现问答 策略模式在JDK源码中的使用总结文章说明 案例引入
有各种鸭子#xff0c;比如野鸭、北京鸭、水鸭等。 鸭子有各种行为#xff0c;比如走路、叫、飞行等。不同鸭子的… 文章目录 案例引入传统方案实现实现分析 介绍基本介绍登场角色 案例实现案例一类图实现 案例二类图实现问答 策略模式在JDK源码中的使用总结文章说明 案例引入
有各种鸭子比如野鸭、北京鸭、水鸭等。 鸭子有各种行为比如走路、叫、飞行等。不同鸭子的行为可能略有不同。要求显示鸭子的信息
传统方案实现
不同的鸭子继承一个父类Duck如果是相同的行为就继承不同行为就重写方法
实现
【鸭子抽象类】
package com.atguigu.strategy;public abstract class Duck {public Duck() {}/*** 显示鸭子信息*/public abstract void display();/*** 叫法*/public void quack() {System.out.println(鸭子嘎嘎叫~~);}/*** 游泳方法*/public void swim() {System.out.println(鸭子会游泳~~);}/*** 飞翔方法*/public void fly() {System.out.println(鸭子会飞翔~~~);}}【野鸭】
package com.atguigu.strategy;public class WildDuck extends Duck {Overridepublic void display() {System.out.println( 这是野鸭 );}}【北京鸭】
package com.atguigu.strategy;public class PekingDuck extends Duck {Overridepublic void display() {System.out.println(~~北京鸭~~~);}/*** 因为北京鸭不能飞翔因此需要重写fly*/Overridepublic void fly() {System.out.println(北京鸭不能飞翔);}}【玩具鸭】
package com.atguigu.strategy;public class ToyDuck extends Duck {Overridepublic void display() {System.out.println(玩具鸭);}//-------需要重写父类的所有方法---------public void quack() {System.out.println(玩具鸭不能叫~~);}public void swim() {System.out.println(玩具鸭不会游泳~~);}public void fly() {System.out.println(玩具鸭不会飞翔~~~);}
}分析
缺点因为继承了Duck所有鸭子都有了会飞的方法虽然可以通过覆盖fly方法来解决但是如果子类很多方法都不需要呢如果每个都要去覆盖一下就很麻烦了改进使用策略模式
介绍
基本介绍
策略模式中定义算法族分别封装到不同的类中让他们之间可以互相替换此模式让算法的变化独立于使用算法的客户。使用策略模式可以整体地替换算法的实现部分让我们可以轻松地以不同的算法去解决同一个问题该模式体现了几个设计原则把变化的代码从不变的代码中分离出来针对接口编程而不是具体类(定义了策略接口)多用组合/聚合少用继承(客户通过组合方式使用策略)
登场角色 Context聚合了策略接口后面需要使用到哪个具体策略的方法就传入该具体策略的实例作为参数即可
Strategy(策略)Strategy角色负责定义实现策略所必需的接口(API)ConcreteStrategy(具体的策略)ConcreteStrategy角色负责实现Strategy角色的接口(API)即负责实现具体的策略(战略、方向、方法和算法)Context(上下文)负责使用Strategy角色。Context角色保存了ConcreteStrategy角色的实例并使用ConcreteStrategy角色即调用Strategy角色的接口去实现需求
案例实现
案例一
类图 实现
【飞翔策略 FlyBehavior】
package com.atguigu.strategy.improve;public interface FlyBehavior {/*** 让子类具体实现*/void fly();
}【叫策略 QuackBehavior】
package com.atguigu.strategy.improve;public interface QuackBehavior {void quack();
}【飞翔技术高超GoodFlyBehavior】
package com.atguigu.strategy.improve;public class GoodFlyBehavior implements FlyBehavior {Overridepublic void fly() {System.out.println( 飞翔技术高超 ~~~);}}【飞翔技术一般BadFlyBehavior 】
package com.atguigu.strategy.improve;public class BadFlyBehavior implements FlyBehavior {Overridepublic void fly() {System.out.println( 飞翔技术一般 );}}【不会飞翔】
package com.atguigu.strategy.improve;public class NoFlyBehavior implements FlyBehavior{Overridepublic void fly() {System.out.println( 不会飞翔 );}}【鸭子抽象类】
package com.atguigu.strategy.improve;public abstract class Duck {/*** 策略接口 飞翔*/FlyBehavior flyBehavior;/*** 策略接口 叫*/QuackBehavior quackBehavior;public Duck() {}/*** 显示鸭子信息*/public abstract void display();public void quack() {System.out.println(鸭子嘎嘎叫~~);}public void swim() {System.out.println(鸭子会游泳~~);}public void fly() {//改进if(flyBehavior ! null) {flyBehavior.fly();}}public void setFlyBehavior(FlyBehavior flyBehavior) {this.flyBehavior flyBehavior;}public void setQuackBehavior(QuackBehavior quackBehavior) {this.quackBehavior quackBehavior;}}【野鸭】
package com.atguigu.strategy.improve;public class WildDuck extends Duck {/*** 构造器传入FlyBehavor 的对象*/public WildDuck() {// 野鸭飞翔技术较强flyBehavior new GoodFlyBehavior();}Overridepublic void display() {System.out.println( 这是野鸭 );}}【北京鸭】
package com.atguigu.strategy.improve;public class PekingDuck extends Duck {/*** 假如北京鸭可以飞翔但是飞翔技术一般*/public PekingDuck() {flyBehavior new BadFlyBehavior();}Overridepublic void display() {System.out.println(~~北京鸭~~~);}}【玩具鸭】
package com.atguigu.strategy.improve;public class ToyDuck extends Duck {public ToyDuck() {// 玩具鸭不会飞翔flyBehavior new NoFlyBehavior();}Overridepublic void display() {System.out.println(玩具鸭);}/*** 需要重写父类的所有方法*/public void quack() {System.out.println(玩具鸭不能叫~~);}public void swim() {System.out.println(玩具鸭不会游泳~~);}}【主类】
package com.atguigu.strategy.improve;public class Client {public static void main(String[] args) {WildDuck wildDuck new WildDuck();wildDuck.fly();ToyDuck toyDuck new ToyDuck();toyDuck.fly();PekingDuck pekingDuck new PekingDuck();pekingDuck.fly();//可以动态改变某个对象的行为, 将北京鸭改成不能飞pekingDuck.setFlyBehavior(new NoFlyBehavior());System.out.println(北京鸭的实际飞翔能力);pekingDuck.fly();}}【输出】
飞翔技术高超 ~~~不会飞翔 飞翔技术一般
北京鸭的实际飞翔能力不会飞翔 Process finished with exit code 0尖叫策略和飞翔策略的实现方式一样这里就不再实现了
案例二
类图 实现
【手势类并不是策略模式的角色】
package com.atguigu.strategy.Sample;/*** 手势*/
public class Hand {/*** 表示石头的值*/public static final int HANDVALUE_GUU 0;/*** 表示剪刀的值*/public static final int HANDVALUE_CHO 1;/*** 表示布的值*/public static final int HANDVALUE_PAA 2;/*** 表示猜拳中3种手势的实例*/public static final Hand[] hand {new Hand(HANDVALUE_GUU),new Hand(HANDVALUE_CHO),new Hand(HANDVALUE_PAA),};/*** 表示猜拳中手势所对应的字符串*/private static final String[] name {石头, 剪刀, 布,};/*** 表示猜拳中出的手势的值*/private int handvalue;private Hand(int handvalue) {this.handvalue handvalue;}/*** 根据手势的值获取其对应的实例这是一种单例模式每种手势只有一个实例** param handvalue* return*/public static Hand getHand(int handvalue) {return hand[handvalue];}/*** 如果this胜了h则返回true** param h* return*/public boolean isStrongerThan(Hand h) {return fight(h) 1;}/*** 如果this输给了h则返回true** param h* return*/public boolean isWeakerThan(Hand h) {return fight(h) -1;}/*** 计分平0, 胜1, 负-1** param h* return*/private int fight(Hand h) {if (this h) {return 0;} else if ((this.handvalue 1) % 3 h.handvalue) {// 当(this.handvalue 1) % 3 h.handvalue时可能得手势组合如下// this是石头h是剪刀// this是剪刀h是布// this是布h是石头return 1;} else {return -1;}}/*** 转换为手势值所对应的字符串** return*/public String toString() {return name[handvalue];}
}【策略接口】
package com.atguigu.strategy.Sample;public interface Strategy {/*** 获取下一局要出的手势* return*/public abstract Hand nextHand();/*** 学习上一局的手势是否获胜了获胜就传进来true否则返回false* param win*/public abstract void study(boolean win);
}【具体策略一】
package com.atguigu.strategy.Sample;import java.util.Random;/*** 该策略是如果上一局赢了这局的手势就和上一局的相同如果上一局输了就随机出*/
public class WinningStrategy implements Strategy {private Random random;/*** 保存上一局是赢还是输了*/private boolean won false;/*** 保存上一局出的手势*/private Hand prevHand;public WinningStrategy(int seed) {random new Random(seed);}public Hand nextHand() {if (!won) {prevHand Hand.getHand(random.nextInt(3));}return prevHand;}public void study(boolean win) {won win;}
}【具体策略二】
package com.atguigu.strategy.Sample;import java.util.Random;public class ProbStrategy implements Strategy {private Random random;private int prevHandValue 0;private int currentHandValue 0;/*** 过去的胜率history[上一局出的手势][这一局所出的手势]* 假设上一局出的手势是石头* history[0][0]两局分别出了石头、石头的获胜次数* history[0][1]两局分别出了石头、剪刀的获胜次数* history[0][2]两局分别出了石头、布的获胜次数* 若history[0][0]3history[0][1]5history[0][2]7* 下一把出什么使用轮盘赌的方式出石头的概率是3/15出剪刀的概率是5/15出布的概率是7/15*/private int[][] history {{ 1, 1, 1, },{ 1, 1, 1, },{ 1, 1, 1, },};public ProbStrategy(int seed) {random new Random(seed);}/*** 学习历史胜率根据轮盘赌的方式来出下一个手势* return*/public Hand nextHand() {int bet random.nextInt(getSum(currentHandValue));int handvalue 0;if (bet history[currentHandValue][0]) {handvalue 0;} else if (bet history[currentHandValue][0] history[currentHandValue][1]) {handvalue 1;} else {handvalue 2;}prevHandValue currentHandValue;currentHandValue handvalue;return Hand.getHand(handvalue);}/*** 获取第一把出hv第二把出1、2、3的总次数* param hv* return*/private int getSum(int hv) {int sum 0;for (int i 0; i 3; i) {sum history[hv][i];}return sum;}/*** 学习经验更新 history 表格* param win*/public void study(boolean win) {if (win) {history[prevHandValue][currentHandValue];} else {history[prevHandValue][(currentHandValue 1) % 3];history[prevHandValue][(currentHandValue 2) % 3];}}
}【游戏选手类】
package com.atguigu.strategy.Sample;/*** 玩猜拳游戏的选手类*/
public class Player {private String name;/*** 记录选手要选用的策略*/private Strategy strategy;/*** 赢的局数*/private int wincount;/*** 输的局数*/private int losecount;/*** 总局数*/private int gamecount;/*** 传入选手的姓名和策略** param name* param strategy*/public Player(String name, Strategy strategy) {this.name name;this.strategy strategy;}/*** 策略决定下一局要出的手势** return*/public Hand nextHand() {return strategy.nextHand();}/*** 猜拳胜利*/public void win() { strategy.study(true);wincount;gamecount;}/*** 猜拳失败*/public void lose() { strategy.study(false);losecount;gamecount;}/*** 猜拳平局*/public void even() { gamecount;}public String toString() {return [ name : gamecount games, wincount win, losecount lose ];}
}【主类】
package com.atguigu.strategy.Sample;public class Main {public static void main(String[] args) {// 让选手分别使用两种策略来比试Player player1 new Player(Taro, new WinningStrategy(314));Player player2 new Player(Hana, new ProbStrategy(12));for (int i 0; i 10000; i) {Hand nextHand1 player1.nextHand();Hand nextHand2 player2.nextHand();if (nextHand1.isStrongerThan(nextHand2)) {
// System.out.println(Winner: player1);player1.win();player2.lose();} else if (nextHand2.isStrongerThan(nextHand1)) {
// System.out.println(Winner: player2);player1.lose();player2.win();} else {
// System.out.println(Even...);player1.even();player2.even();}}System.out.println(Total result:);System.out.println(player1.toString());System.out.println(player2.toString());}
}【运行】
Total result:
[Taro:10000 games, 3107 win, 3617 lose]
[Hana:10000 games, 3617 win, 3107 lose]Process finished with exit code 0问答 如果需要增加一个随机出手势的策略需要怎么实现 答在nextHand方法中使用随机数即可因为全部都是随机的不需要学习之前的经验因此study方法可以是空方法 在示例程序中Hand类的fight方法负责判断平局。在进行判断时它使用的表达式不是this.handValue h.value而是thish请问为什么可以这样写? 答因为使用了单例模式只有三个手势实例如果两个手势的handValue相同说明两个实例就是同一个实例 编写Winningstrategy类时won 字段的定义不是private boolean won false; 而是写成了如下这样private boolean won;虽然写法不同但是两者的运行结果一样为什么 答因为全局变量如果没有被赋值就会被自动初始化boolean类型默认是false数值类型默认是0引用类型默认是null。注意局部变量不会被自动初始化
策略模式在JDK源码中的使用
简单来说就是在排序的时候可以指定不同的排序策略
package com.atguigu.jdk;import java.util.Arrays;
import java.util.Comparator;public class Strategy {public static void main(String[] args) {// TODO Auto-generated method stub//数组Integer[] data {9, 1, 2, 8, 4, 3};// 实现降序排序返回-1放左边1放右边0保持不变// 说明// 1. 实现了 Comparator 接口策略接口 , 匿名类对象new ComparatorInteger(){..}// 2. 对象 new ComparatorInteger(){..} 就是实现了 策略接口 的对象// 3. public int compare(Integer o1, Integer o2){} 指定具体的处理策略ComparatorInteger comparator new ComparatorInteger() {public int compare(Integer o1, Integer o2) {if (o1 o2) {return -1;} else {return 1;}}};// sort源码说明 传入数字和一个排序策略/** public static T void sort(T[] a, Comparator? super T c) {if (c null) {sort(a); //默认方法} else {if (LegacyMergeSort.userRequested)legacyMergeSort(a, c); //使用策略对象celse// 使用策略对象cTimSort.sort(a, 0, a.length, c, null, 0, 0);}}*/// 方式1Arrays.sort(data, comparator);// 降序排序System.out.println(Arrays.toString(data));//方式2- 同时lambda 表达式实现 策略模式Integer[] data2 {19, 11, 12, 18, 14, 13};// 换一个排序策略Arrays.sort(data2, (var1, var2) - {if (var1.compareTo(var2) 0) {return -1;} else {return 1;}});System.out.println(data2 Arrays.toString(data2));}}总结
【说明】
策略模式的关键是分析项目中变化部分与不变部分
【优点】
策略模式的核心思想是多用组合/聚合少用继承用行为类来组合而不是行为的继承体现了“对修改关闭对扩展开放”原则客户端增加行为不用修改原有代码只要添加一种策略 (或者行为)即可避免了使用多重判断语句 (if…else if…else)提供了可以替换继承关系的办法: 策略模式将算法封装在独立的Strategy类中使得你可以独立于其他Context改变它使它易于切换、易于理解、易于扩展程序运行过程中也可以切换策略如果使用Strategy模式在程序运行中也可以切换ConcreteStrategy角色。如在内存较少的环境使用一种算法内存较多的环境使用另外一种算法
【缺点】
每添加一个策略就要增加一个类当策略过多是会导致类数目庞大
【问答】 为什么需要特意编写Strategy角色 答当我们想要通过改善算法来提高算法的处理速度时如果使用了Strategy模式就不必修改Strategy角色的接口(API)了仅仅修改ConcreteStrategy角色即可。而且使用委托这种弱关联关系可以很方便地整体替换算法这样也更加方便算法的比较
文章说明
本文章为本人学习尚硅谷的学习笔记文章中大部分内容来源于尚硅谷视频点击学习尚硅谷相关课程也有部分内容来自于自己的思考发布文章是想帮助其他学习的人更方便地整理自己的笔记或者直接通过文章学习相关知识如有侵权请联系删除最后对尚硅谷的优质课程表示感谢。本人还同步阅读《图解设计模式》书籍图解设计模式/(日)结城浩著;杨文轩译–北京:人民邮电出版社2017.1进而综合两者的内容让知识点更加全面