现在工作不忙,买了本head first 设计模式的书看看,看着想睡觉,所以想以博客的形式分享出来,可能写的不好,毕竟我也是刚学,但没关系,希望大家在发现错误的时候能改正,技术在于分享,设计模式虽然不是什么高深的技术,但是要在实际的项目中玩的很溜也不容易啊 ,在很多好点的开源项目中都用到了设计模式,所以学好设计模式很重要,本人也是菜鸟一枚希望和大家一起进步 ,发大财!努力学习模式吧,骚年!
引入:
Joe上班的公司做了一套相当成功的模拟鸭子游戏:SimUDuck.游戏中会出现各种鸭子,一边游泳戏水,一边呱呱叫。此系统的内部设计使用了标准的oo(面向对象)技术,设计了一个鸭子超类(superclass),并让各种鸭子继承此超类。
分析:每个鸭子都有其共同的特点,而也有不同之处,所以我们得设计一个类把共同的特点抽取出来,而不同之处让其每个鸭子自己去实现,在java中也是让每个子类自己去实现,共同的特点就是鸭子会游泳,会呱呱叫,而不同点在于每个鸭子的外观是不一样的,在这我们把鸭子的超类命名为Duck,他里面有quck(呱呱叫方法),swim(游泳方法),display(鸭子外观显示方法,是一个抽象方法,因为每个鸭子外观都不一样)
代码:
Duck
public abstract class Duck { public void quack(){ } public void swim(){ } public abstract void display(); }
MallardDuck.java:
package cn.kge.pattern1; /** * MallardDuck继承了抽象类Duck * 复写了抽象类中的抽象方法 display */ public class MallardDuck extends Duck { @Override public void display() { System.out.println(" MallardDuck------display "); } }
package cn.kge.pattern1; public class RedheadDuck extends Duck { @Override public void display() { System.out.println(" RedheadDuck------display "); } }
Duck.java
package cn.kge.pattern1; public abstract class Duck { public void quack(){ } public void swim(){ } public void fly(){ } public abstract void display(); }
为了复用的目的而使用继承结局并不完美,如果这个系统越大越难维护,请不要叫我码农,请叫我工程师,因为我会改bug,改方案!这就是像我这样屌丝工程师存在的价值,这个时候我们就不要使用继承了,就使用接口就行了,如果那个鸭子需要飞这个行为,就实现这个接口,而不需要的就不要实现这个接口就行,如果这个时候有哦个诱饵鸭,它不会飞也不会叫,那么这个时候我们继承父类的quack和swim方法 就什么都不用写了,也就是在方法中没有具体的业务逻辑代码了,写在这里不是多余么,而且还不方便后期人员读懂代码,而使用接口会造成代码无法复用,每个鸭子都要重写一篇一样的代码,这肯定是要重构的,所以使用接口也有它的缺点所在,
问题总结:现在我们知道使用继承并不能很好地解决问题,因为鸭子的行为在子类里不断地改变,并且让所有的子类都有这些行为是不恰当的,Flyable与Quackable接口一开始视乎还挺不错,解决了问题,但是java接口不具备实现代码,所有继承接口无法达到代码的复用,这就意味着:无论何时你需要修改某个行为,你必须得往下追踪并在每一个定义此行为的类中修改它,一不小心可能会造成新的错误! 幸运的是,有一个设计原则,恰好使用这个状况,
设计原则之一:
找出应用中可能需要变化之外,把它们独立出来,不要和那些不需要变化的代码混在一起。
分开变化和不会变化的部分
但是我们从哪开始呢?就我们目前所知,除了fly()和quack()的问题之外,Duck类还算一切正常,似乎没有特别需要经常变化或者修改的地方,所以,除了某些小改变之外,我们不打算对Duck类做太多处理,
现在,为了要分开"变化和不会变化的部分",我们准备建立二组类(完全远离Duck类),一个是"fly"相关的,一个时"quack”相关的,每一组类将实行各自的工作,比方说,我们可能有一个实现"呱呱叫",另一个类实现"吱吱叫",还有一个类实现"安静",
我们知道Duck类内的fly()和quack()会随着鸭子的不同而改变.
为了要把这二个行为从Duck类中分开,我们将把它们从Duck类中取出来,建立一组新类来代表每个行为
设计鸭子的行为
如何设计那组实现飞行和呱呱叫的行为类呢?
但是设计出来的要有后期的可维护性和可扩展性,比方说,我们想要产生一个新的绿头鸭实例,并指定特定"类型"的飞行行为给它,顺便让鸭子的行为可以动态地改变好了,换句话说,我们应该在鸭子类中包含设定行为的方法,这样就可以在"运行时"动态地"改变"绿头鸭的飞行行为,
有了这些目标要实现,我们就要看看设计原则的第二个设计原则:
针对接口编程,而不是针对实现编程!
在java中行为都是动词,表示类的行为,而在java中对应的是方法,我们利用接口代表每个行为,比方说,FlyBehavior与QuackBehavior,而行为的每个实现都将实现其中的一个接口,
针对接口编程含义:
这里所谓的"接口" 有多个含义,接口是一个"概念",也是一种java的interface构造,你可以在不涉及java interface的情况下,针对接口编程,关键就在多态,利用多态,程序可以针对父类类型编程,执行时会根据实际状况执行真正的行为,不会绑死在超类类型的行为下,"针对超类类型编程"这句话,可以明确地说成"变量的声明类型应该是超类型",通常是一个抽象类或者是一个接口,如此,只要是具体实现此超类型的类所产生的对象,都可以指定给这个变量,
下面举个例子讲下针对接口编程
Animal(抽象类) Dog与Cat是Animal的二个具体实现类,
package cn.kge.pattern2; public class Dog { public void makeSound(){ System.out.println("哇哇---"); } }
package cn.kge.pattern2; public class Test { public static void main(String[] args) { /** * 声明变量 d 为Dog类型会造成我们必须针对具体实现编码 */ Dog d = new Dog(); d.makeSound(); } }
Animal.java
public abstract class Animal { public abstract void makeSound(); }
package cn.kge.pattern2; public class Dog extends Animal{ @Override public void makeSound() { } }
package cn.kge.pattern2; public class Test { public static void main(String[] args) { /** * 声明变量 d 为Animal类型 但是利用多态 其实是调用Dog中的方法 */ Animal d = new Dog(); d.makeSound(); } }
在此,我们有二个接口,FlyBehavior和QuackBehavior,还有他们对应的类,负责实现具体的行为:
FlyWithWings和FlyNoWay类都是实现了FlyBehavior接口,
设计:
FlyBehavior.java
package cn.kge.pattern2; public interface FlyBehavior { void fly(); }FlyWithWings.java
package cn.kge.pattern2; public class FlyWithWings implements FlyBehavior { @Override public void fly() { //实现鸭子飞行 } }
package cn.kge.pattern2; public class FlyNoWay implements FlyBehavior { @Override public void fly() { //什么都不做,不会飞 } }
package cn.kge.pattern2; public interface QuackBehavior { void quack(); }
package cn.kge.pattern2; public class Quack implements QuackBehavior { @Override public void quack() { //实现鸭子呱呱叫 } }
package cn.kge.pattern2; public class Squeak implements QuackBehavior { @Override public void quack() { //玩具鸭子吱吱叫 } }
package cn.kge.pattern2; public class MuteQuack implements QuackBehavior { @Override public void quack() { //什么都不做 ,不会叫 } }