告诉你什么是策略模式(Strategy)"/>
让GTA 5来告诉你什么是策略模式(Strategy)
目录
了解策略模式
让GTA 5给你展示策略模式
咱们自己从代码上实现:
鼻子策略族
鼻子策略接口:
大鼻子——鼻子具体策略1
高鼻子——鼻子具体策略2
小鼻子——鼻子具体策略3
眉毛策略族
眉毛策略接口:
平直眉——眉毛具体策略1
上挑眉——眉毛具体策略2
一字眉——眉毛具体策略3
眼睛策略族
眼睛策略接口:
黑眼睛——眼睛具体策略1
蓝眼睛——眼睛具体策略2
紫眼睛——眼睛具体策略3
嘴策略族
嘴策略接口:
爱心唇——嘴具体策略1
覆舟唇——嘴具体策略2
厚嘴唇——嘴具体策略3
写角色:
角色父类:
策略角色1
策略角色2
测试——选择角色:
输出结果:
思考分析
1. 代码复用性高
2. 扩展性好
了解策略模式
策略模式(Strategy):它定义了算法家族,分别封装起来,让它们之间可以相互替换,此模式让算法的变化,不会影响到用算法的客户。
这句话怎么理解呢?
简单来讲,策略模式就是把“相似功能的算法”,或者叫做“功能相似的方法”,每个方法都独立出来写成个类。使用的时候,想用那个,就直接调用想相应的类方法就行。
然后这些相似功能的类,用一个接口去管理,这就叫算法家族了。
让GTA 5给你展示策略模式
玩过GTA 5线上模式的人应该都有捏脸的经历:
游戏截图,
鼻子+眼+眉毛+嘴 =》脸型
选择不同的鼻子、眉毛、眼、嘴,就是不同的策略,然后这些策略构成人物模型。
咱们自己从代码上实现:
鼻子、眉毛、眼、嘴,咱们每种选择3个类型(策略),来实现。
具体实现基本相同,看鼻子一个算法族的就够了。
以下开始写策略。
鼻子策略族
鼻子策略接口及其实现。
鼻子策略接口:
package 策略模式.鼻子;public interface 鼻子 {String 鼻子类型();
}
大鼻子——鼻子具体策略1
package 策略模式.鼻子;public class 大鼻子 implements 鼻子 {@Overridepublic String 鼻子类型() {return "大鼻子";}
}
高鼻子——鼻子具体策略2
package 策略模式.鼻子;public class 高鼻子 implements 鼻子 {@Overridepublic String 鼻子类型() {return "高鼻子";}
}
小鼻子——鼻子具体策略3
package 策略模式.鼻子;public class 小鼻子 implements 鼻子 {@Overridepublic String 鼻子类型() {return "小鼻子";}
}
眉毛策略族
眉毛策略接口及其实现。
眉毛策略接口:
package 策略模式.眉毛;public interface 眉毛 {String 眉毛类型();
}
平直眉——眉毛具体策略1
package 策略模式.眉毛;public class 平直眉 implements 眉毛 {@Overridepublic String 眉毛类型() {return "平直眉";}}
上挑眉——眉毛具体策略2
package 策略模式.眉毛;public class 上挑眉 implements 眉毛 {@Overridepublic String 眉毛类型() {// TODO 自动生成的方法存根return "上挑眉";}}
一字眉——眉毛具体策略3
package 策略模式.眉毛;public class 一字眉 implements 眉毛 {@Overridepublic String 眉毛类型() {// TODO 自动生成的方法存根return "一字眉";}}
眼睛策略族
眼睛策略接口及其实现。
眼睛策略接口:
package 策略模式.眼睛;public interface 眼睛 {String 眼睛类型();
}
黑眼睛——眼睛具体策略1
package 策略模式.眼睛;public class 黑眼睛 implements 眼睛 {@Overridepublic String 眼睛类型() {// TODO 自动生成的方法存根return "黑眼睛";}}
蓝眼睛——眼睛具体策略2
package 策略模式.眼睛;public class 蓝眼睛 implements 眼睛 {@Overridepublic String 眼睛类型() {// TODO 自动生成的方法存根return "蓝眼睛";}}
紫眼睛——眼睛具体策略3
package 策略模式.眼睛;public class 紫眼睛 implements 眼睛 {@Overridepublic String 眼睛类型() {// TODO 自动生成的方法存根return "棕眼睛";}}
嘴策略族
嘴策略接口及其实现。
嘴策略接口:
package 策略模式.嘴;public interface 嘴 {String 嘴型();
}
爱心唇——嘴具体策略1
package 策略模式.嘴;public class 爱心唇 implements 嘴 {@Overridepublic String 嘴型() {// TODO 自动生成的方法存根return "爱心唇";}}
覆舟唇——嘴具体策略2
package 策略模式.嘴;public class 覆舟唇 implements 嘴 {@Overridepublic String 嘴型() {// TODO 自动生成的方法存根return "覆舟唇";}}
厚嘴唇——嘴具体策略3
package 策略模式.嘴;public class 厚嘴唇 implements 嘴 {@Overridepublic String 嘴型() {// TODO 自动生成的方法存根return "厚嘴唇";}}
写角色:
以上已经制定好各种策略,从这里就开始展示怎么使用各种策略了。
角色父类:
package 策略模式.角色;import 策略模式.嘴.嘴;
import 策略模式.眉毛.眉毛;
import 策略模式.眼睛.眼睛;
import 策略模式.鼻子.鼻子;public abstract class 策略角色 {鼻子 bz;眉毛 mm;眼睛 yj;嘴 z;public 策略角色() {}public String 鼻子类型() {return bz.鼻子类型();}public String 眉毛类型() {return mm.眉毛类型();}public String 眼睛类型() {return yj.眼睛类型();}public String 嘴类型() {return z.嘴型();}
}
策略角色1
package 策略模式.角色;import 策略模式.嘴.爱心唇;
import 策略模式.眉毛.平直眉;
import 策略模式.眼睛.黑眼睛;
import 策略模式.鼻子.大鼻子;public class 策略角色1 extends 策略角色{public 策略角色1() {bz = new 大鼻子();mm = new 平直眉();yj = new 黑眼睛();z = new 爱心唇();}
}
策略角色2
package 策略模式.角色;import 策略模式.嘴.爱心唇;
import 策略模式.眉毛.平直眉;
import 策略模式.眼睛.黑眼睛;
import 策略模式.鼻子.高鼻子;public class 策略角色2 extends 策略角色{public 策略角色2() {bz = new 高鼻子();mm = new 平直眉();yj = new 黑眼睛();z = new 爱心唇();}
}
以上已经使用策略写了两个角色,接下来咱们进行测试使用。
测试——选择角色:
package 策略模式;import 策略模式.角色.策略角色1;
import 策略模式.角色.策略角色2;
import 策略模式.角色.非策略角色1;
import 策略模式.角色.非策略角色2;public class 选择角色 {public static void main(String[] args) {System.out.println("========");System.out.println("策略选择");System.out.println("========");策略角色1 one = new 策略角色1();System.out.println(one.鼻子类型()+"\n"+one.眉毛类型()+"\n"+one.眼睛类型()+"\n"+one.嘴类型());System.out.println("--------");策略角色2 two = new 策略角色2();System.out.println(two.鼻子类型()+"\n"+two.眉毛类型()+"\n"+two.眼睛类型()+"\n"+two.嘴类型());}
}
输出结果:
========
策略选择
========
大鼻子
平直眉
黑眼睛
爱心唇
--------
高鼻子
平直眉
黑眼睛
爱心唇
思考分析
为什么要用策略模式,都是一个角色一个类,为什么不直接写,反而把方法都写成一个类,还写接口,变得那么复杂?
1. 代码复用性高
其实这个很好理解。
我们想一下,为什么java要封装方法?直接在main方法里边写下去不久行了?
这个问题,相信很多人都已经有一定的理解了——为了提高代码的复用性,把常用的代码封装成方法,下次使用不用再写,直接调用就行。
来个小案例,加深一下理解。
假设我们有个需求:我们要分别对5个大小为5的数组进行比较,然后分别输出每个数组的最大值。
不封装成方法时,我们的代码是这样的:
package 策略模式.为什么封装方法;public class 方法封装 {Integer[] a = {5,9,7,6,1};Integer[] b = {6,8,5,3,4};Integer[] c = {0,4,6,2,7};Integer[] d = {8,4,9,2,5};Integer[] e = {1,6,2,0,5};public 方法封装() {sort();}private void sort() {int j=0;// 对a进行排序for (int i = 0; i < a.length-1; i++) {if(a[i]>a[i+1]) {j=a[i+1];a[i+1]=a[i];a[i]=j;}}System.out.println(a[4]);// 对b进行排序for (int i = 0; i < b.length-1; i++) {if(b[i]>b[i+1]) {j=b[i+1];b[i+1]=b[i];b[i]=j;}}System.out.println(b[4]);// 对c进行排序for (int i = 0; i < c.length-1; i++) {if(c[i]>c[i+1]) {j=c[i+1];c[i+1]=c[i];c[i]=j;}}System.out.println(c[4]);// 对d进行排序for (int i = 0; i < d.length-1; i++) {if(d[i]>d[i+1]) {j=d[i+1];d[i+1]=d[i];d[i]=j;}}System.out.println(d[4]);// 对e进行排序for (int i = 0; i < e.length-1; i++) {if(e[i]>e[i+1]) {j=e[i+1];e[i+1]=e[i];e[i]=j;}}System.out.println(e[4]);}public static void main(String[] args) {new 方法封装();}}
很明显,赤裸裸的粘贴复制了4次!
封装一下,代码是这样的:
package 策略模式.为什么封装方法;public class 方法封装 {Integer[] a = {5,9,7,6,1};Integer[] b = {6,8,5,3,4};Integer[] c = {0,4,6,2,7};Integer[] d = {8,4,9,2,5};Integer[] e = {1,6,2,0,5};public 方法封装() {sort(a);sort(b);sort(c);sort(d);sort(e);}private void sort(Integer[] a) {int j=0;// 对a进行排序for (int i = 0; i < a.length-1; i++) {if(a[i]>a[i+1]) {j=a[i+1];a[i+1]=a[i];a[i]=j;}}System.out.println(a[4]);}public static void main(String[] args) {new 方法封装();}}
现在直观的明白封装方法的好处了吧?
对应到类之间,如果类做的事都是同一件事。那代码重复率岂不是跟上边不使用方法那样?
咱们来做一个不使用策略模式创建角色和使用策略对比下。
不使用策略:
非策略角色父类
package 策略模式.角色;public abstract class 非策略角色 {public String 鼻子类型() {return "";}public String 眉毛类型() {return "";}public String 眼睛类型() {return "";}public String 嘴类型() {return "";}
}
非策略角色1
package 策略模式.角色;public class 非策略角色1 extends 非策略角色{@Overridepublic String 鼻子类型() {return "大鼻子";}@Overridepublic String 眉毛类型() {return "平直眉";}@Overridepublic String 眼睛类型() {return "黑眼睛";}@Overridepublic String 嘴类型() {return "爱心唇";}
}
非策略角色2
package 策略模式.角色;public class 非策略角色2 extends 非策略角色{@Overridepublic String 鼻子类型() {return "高鼻子";}@Overridepublic String 眉毛类型() {return "平直眉";}@Overridepublic String 眼睛类型() {return "黑眼睛";}@Overridepublic String 嘴类型() {return "爱心唇";}
}
看看,不使用策略模式,就只能复写父类的方法。哪怕只改动了鼻子类型,其他的也都需要复写。
我们只使用了4个部位,3中选择的小案例。
假如我们要把所有的可能都写出来。
那么,策略模式需要写:4*3=12个方法+3*3*3*3=81次调用。
非策略需要写:4个父类方法+3*3*3*3=81个子类复写方法。
相信大家已经很明确的知道了,策略模式,代码复用性好的优点。
2. 扩展性好
现在有个需求,角色3需要“柳叶眉”
我们需要怎么做?
再写个实现眉毛接口的实现类就行了!
package 策略模式.眉毛;public class 柳叶眉 implements 眉毛 {@Overridepublic String 眉毛类型() {return "柳叶眉";}}
然后,新建角色时,就可以直接选择了:
策略角色3
package 策略模式.角色;import 策略模式.嘴.爱心唇;
import 策略模式.眉毛.柳叶眉;
import 策略模式.眼睛.黑眼睛;
import 策略模式.鼻子.高鼻子;public class 策略角色3 extends 策略角色{public 策略角色3() {bz = new 高鼻子();mm = new 柳叶眉();yj = new 黑眼睛();z = new 爱心唇();}
}
缺点
谈完优点,再来说缺点:
其实,他的优点也是缺点。
当策略很多时,我们要写的实现类太多了。
而且,因为策略都要提供被选择,所以所有的策略都是public——暴露的!
个人所学和总结,如果那里不对,希望看到的大佬指正批评!
更多推荐
让GTA 5来告诉你什么是策略模式(Strategy)
发布评论