java 欢迎进入设计模式世界之前凑

现在工作不忙,买了本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,RedheadDuck继承了Duck,

MallardDuck.java:

package cn.kge.pattern1;
/**
 * MallardDuck继承了抽象类Duck
 * 复写了抽象类中的抽象方法 display
 */
public class MallardDuck extends Duck  {
	@Override
	public void display() {
		System.out.println(" MallardDuck------display  ");
	}
}

RedheadDuck.java

package cn.kge.pattern1;

public class RedheadDuck extends Duck {
	@Override
	public void display() {
		System.out.println(" RedheadDuck------display  ");
	}
}

可能你还会暗暗得意,这系统或者框架设计的很牛逼,突然一天你boss说,码农此模拟程序需要会飞的鸭子,这个时候你很得意的在boss面前说这个一天搞定,只要在Duck(抽象类中添加一个fly)方法即可,

Duck.java

package cn.kge.pattern1;

public abstract class Duck {
	public  void quack(){
	}
	public  void swim(){
	}
	public  void fly(){
	}
	public abstract void display();
}

而其他子类只能复写fly()方法即可,这个需求就搞定了,如果这个时候被老板看到你这么写的话,估计你就准备投简历找工作了,难道我一个玩具鸭子也会飞,于是乎你就思考人生吧,如果直接在父类(Duck)中添加了fly方法,那么那些(像玩具鸭子)也会具有飞的行为,那么这肯定是不符合逻辑的,虽然你只是在父类中添加了一个方法,但是这会导致其他继承他的子类都会具有的行为,这就是对代码所做的局部修改,影响层面可不只是局部。这个时候码农深深的体会到了一件事,

为了复用的目的而使用继承结局并不完美,如果这个系统越大越难维护,请不要叫我码农,请叫我工程师,因为我会改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("哇哇---");
     }
}

Test.java

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();
}

Dog继承此Animal

package cn.kge.pattern2;
public class Dog extends Animal{
	@Override
	public void makeSound() {
		
	}
}

Test.java

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() {
         //实现鸭子飞行
	}
}

FlyNoWay.java

package cn.kge.pattern2;
public class FlyNoWay   implements FlyBehavior {
	@Override
	public void fly() {
		//什么都不做,不会飞
	}
}

QuackBehavior.java 呱呱叫的行为

package cn.kge.pattern2;
public interface QuackBehavior {
		void quack();
}

Quack.java

package cn.kge.pattern2;
public class Quack implements QuackBehavior {
	@Override
	public void quack() {
		//实现鸭子呱呱叫
	}
}

Squeak.java

package cn.kge.pattern2;
public class Squeak implements QuackBehavior {
	@Override
	public void quack() {
		//玩具鸭子吱吱叫
	}
}

MuteQuack.java

package cn.kge.pattern2;
public class MuteQuack implements QuackBehavior {
	@Override
	public void quack() {
		//什么都不做 ,不会叫
	}
}

这样一来 根据很方面的实现后期的维护和扩展,比如要什么鸭子会发的就继承FlyWithWings,不要实现飞行的就继承FlyNoWay类,要实现鸭子呱呱叫的就继承Quack类,这样一来,有了继承的复用好处,却没有刚开始继承带来的种种问题,比如什么不需要飞的鸭子也继承了fly的方法,







 





你可能感兴趣的:(java,android)