前一段时间在朋友的极力推荐下,有幸拜读了head.first 的大作《
设计模式》,阅读了几章之后猛然
发现,原来技术文章也可以写的如此通俗,优雅。遂想将个人的一些读后感和对书中设计模式的
理解整理成文,与众
程序员朋友分享。同时,也希望您能慷慨的发表自己的看法与理解,帮助新晋的程序员朋友在软件开发之路上走的更加从容。如果您正好看过此书,请一定不吝赐教。欢迎大家拍砖!
下面是我对书中策略模式的讲解的分析:
场景:一款模拟鸭子的游戏,游戏中出现各种鸭子,一边戏水,一边呱呱叫
描述:有一个鸭子的抽象类,各种鸭子继承此类
鸭子种类:红头鸭,绿头鸭,木头假鸭,橡皮鸭
抽象类的方法:quack(),swim(),abstract display()
补充:抽象类中已对quack和swim进行了实现。鸭子正常叫为呱呱叫,红头与绿头鸭呱呱叫,木头鸭不会叫,橡皮鸭会吱吱叫
此设计优点:子类可复用quack(),swim()方法
此设计缺点:个别种类的鸭子需要覆盖父类的quack方法满足自己的需求(例如木头鸭不会叫)
public abstract class Duck{
public void quack(){
System.out.println("呱呱叫");
}
public void swim(){
System.out.println("戏水");
}
public abstract void display();
}
需求:需要某些鸭子能够飞行
方案一:在超类中增加fly()方法并对其实现
缺点:所有的鸭子都拥有了fly()的功能,哪怕本身不能fly(),凡是增加新的鸭子类都需要对fly()方法进行检测,如不需要则必须覆盖
public abstract class Duck{
public void quack(){
System.out.println("呱呱叫");
}
public void swim(){
System.out.println("戏水");
}
public void fly(){
System.out.println("飞行");
}
public abstract void display();
}
方案二:将fly()方法抽出写入
接口,需要的自行实现该接口
缺点:无法复用,重复代码增多
public interface Flyable{
public void fly();
}
症结:鸭子的某些行为在子类中会不断的变化,让所有子类都拥有这类的行为是不恰当的
设计原则:(封装变化)找出应用中可能需要变化之处,把它们独立出来,和不需要改变的分开,以便它们可以独立的修改和扩展而不影响其他部分
解答:将fly,quack方法抽出为两组独立的类型,并根据需要进行不同的实现。例如,quack类型组可以包含,安静,呱呱叫,吱吱叫
public interface Quack{
public void quack();
}
public interface Flyable{
public void fly();
}
public class FlyWithWing implements Flyable{
public void fly(){
System.out.println("用翅膀飞行");
}
}
public class ZhiZhiQuack implements Quack{
public void quack(){
System.out.println("吱吱叫");
}
}
public class GuaGuaQuack implements Quack{
public void quack(){
System.out.println("呱呱叫");
}
}
补充:由于一开始鸭子行为设计的没有弹性才出现了以上的情况,所以应该采用一种弹性的方案。可以根据需要动态改变鸭子的行为
设计原则:(鸭子类不在拥有具体的行为实现方法,只拥有行为的引用接口)针对接口编程,而不是针对实现编程
解答:鸭子的行为放在分开的类中,让该类提供某行为接口的实现
设计原则:多用组合,少用继承(鸭子的行为不再是继承而来,而是组合而来)
设计模式:策略模式,定义了
算法族(鸭子的行为类型组),分别封装起来,让
他们之间可以相互替换,此模式让算法的变化(鸭子的行为)独立于使用算法的客户(鸭子)
public abstract class Duck{
private Quack quack;
private Flyable fly;
public void executeQuack(){
quack.quack();
}
public void executeFly(){
fly.fly();
}
public void setQuack(Quack q){
this.quack=q;
}
public void setFly(Flyable f){
this.fly=f;
}
public void swim(){
System.out.println("戏水");
}
public abstract void display();
}
子类在构造时设定具体的行为即可,并且在运行时也可动态改变。
下面是我对此模式中所使用的一些软件设计原则的看法与感悟:
封装变化:
1.为每种行为的变化定义接口
2.将每种具体的行为封装为类并实现该种行为的接口
解释:本例中的变化的类型是鸭子的行为,包含fly,quack两组。分别对其定义接口,再根据具体的需求予以实现。例如quack接口包含(呱呱叫,吱吱叫,无声)三种具体的实现
针对接口编程:
1.将具体实现委托给接口
2.问题域类只负责接口级别的逻辑控制
3.问题域类的
责任依赖于持有的抽象接口而不是具体实现
解释:鸭子类只持有行为的接口,本身不负责实现。对鸭子类进行了
细粒度的抽象,将其具体的行为予以抽离,该问题域类(鸭子)的责任只依赖于内部所持有的接口
多用组合,少用继承:
1.通过组合的方式构建类的行为,而不是继承。可以使类更加灵活,在运行期可动态改变行为
以上便是我的理解,如有不对或者疑惑之处欢迎指出!文中只写出主要的代码,其他代码均已省略,若有需要的朋友可自行参阅该书,亦可发站内信向我索取!