首先来想一个问题:软件开发完成’前’和软件开发完成’后’哪个需要花费更多的时间?
答案是:软件开发后
那么来想想为什么软件开发后为更需要花费时间呢?
因为我们总是需要花费更多的时间在系统的维护和变化上,比之前开发需要的时间还要长.
所以我们要致力于可维护性和可发展性这个时候我们需要用到设计模式
设计模式需要遵循一些原则
我们来举一个例子,说一个游戏公司要做一个模拟鸭子的游戏,我们要去怎么设计呢?
鸭子通用的有会游泳和呱呱叫,所以我们可以把这两个鸭子特有的功能抽象成一个超类.
其他鸭子继承这个超类也就有了这两个功能.
public abstract class Duck {
void quack(){//鸭子叫
System.out.println("鸭子呱呱叫");
}
void swim(){//鸭子都会游泳
}
public abstract void exterior();//外观设为抽象的
}
public class GreenHeadDuck extends Duck{
@Override
public void exterior() {
System.out.println("绿头鸭子头是绿色的");
}
}
public class ReadHeadDuck extends Duck{
@Override
public void exterior() {
System.out.println("红头鸭子头是红色的");
}
}
然而一开始我们这么设计是什么问题都没有的.
这个时候呢老板忽然说我们要为这个鸭子增加一个可以飞的功能.我们的主管说这还不简单看我给你设计的
下面是他重新设计的
本来我们是打算准备去庆祝聚餐不料到这个时候主管接到了老板的电话,只听老板和他说了句,你可以离职了
原来是老板在会议给股东展示观看的时候发现一只”橡皮鸭子”在屏幕上飞来飞去
我们先来看看他犯了哪些错误
之所以会这样是因为这个主管忽略了一件事,不同于鸭子叫和会游泳不是所有鸭子都会飞的,和考虑到扩展性
以后可能老板还要多加鸭子,那么没增加一个鸭子就要覆盖他的叫声这个方法.
主管首先想到用继承 这样可以覆盖掉Duck鸭子的fly()方法,让他什么也不做,覆盖叫声变成吱吱叫
/**
* @author wang
* 橡皮鸭子
*/
public class RubberDuck extends Duck{
@Override
public void exterior() {
System.out.println("我是橡皮鸭子");
}
public void quack(){//重写叫声方法 变成吱吱叫
System.out.println("我的叫声是吱吱声");
}
public void fly(){//重写fly方法什么也不做
}
public static void main(String[] args) {
RubberDuck d = new RubberDuck();
d.fly();
}
}
为此我们得出了一个结论继承并不适合所有复用.
之后技术经理竟然把主管的这个决定给否决了认为这个不行,这样子要是太多每个都覆盖太浪费精力了(每当有新鸭子出现就要重新覆盖),技术经理给出了一个更好的办法 使用接口.
把fly 和quack 单独抽取出来 放到接口中 我们分别创建两个接口,剩下的放在超类这样不就可以了么
但是这有是一个超笨的解决办法,因为我们知道接口不具有实现代码无法达到代码的复用,导致大量的重复代码
所以我们需要一个最终的解决方案.
第一个原则:设计原则
就是把不需要改变的地方,单独抽取出来进行’封装’,让其他部分不受影响
鸭子的飞的行为和叫的行为是会发生变化的进行抽取的其他的Duck类的不会变化的保持不变
我们需要鸭子的行为具有’弹性’可以动态的进行改变这个时候需要第二个原则.
第二个原则:针对接口编程,而不是针对实现进行编程
新设计上,鸭子的子类使用接口,所表示行为(具体的行为是靠鸭子的接口实现类实现的)
为什么要使用接口呢?
关键在于多态,利用多态程序可以针对超类型编程,执行时候会根据实际情况执行相应的实现类,
开始整合行为
/**
* @author wang
* 鸭子的超类
*/
public abstract class Duck {
Flyable flyable;//引用实现飞行的对象
public void PerFormPly(){//鸭子对象不处理飞行行为,交给Flyable的实现对象去处理
flyable.Fly();
}
Quackable quackable;
public void PerFormQuack(){
quackable.quack();
}
void swim(){//鸭子都会游泳
System.out.println("开始游泳了");
}
abstract void exterior();
}
/**
* @author wang 飞行的接口
*/
public interface Flyable {
public void Fly();
}
/**
* @author wangy 不会飞的鸭子实现类
*/
public class FlyNoWay implements Flyable {
@Override
public void Fly() {
System.out.println("不会飞的鸭子");
}
}
/**
* @author wang 飞行鸭实现类
*/
public class FlyWithWay implements Flyable {
@Override
public void Fly() {
System.out.println("会飞行的鸭子");
}
}
/**
* @author wangy 绿头鸭
*/
public class GreenHeadDuck extends Duck {
// 因为继承了Duck所以有Duck声明的两个实例
public GreenHeadDuck() {
quackable = new Quack();// 真鸭子叫
flyable = new FlyWithWay();// 会飞
}
@Override // 重写外观
void exterior() {
System.out.println("我是一只绿头鸭");
}
}
/**
* @author wang 测试类
*/
public class MiniDuck {
public static void main(String[] args) {
GreenHeadDuck g = new GreenHeadDuck();
g.exterior();
g.PerFormPly();
g.PerFormQuack();
}
}
/**
* @author wang 不会叫的鸭子实现类
*/
public class MuteQuack implements Quackable {
@Override
public void quack() {
// 不会叫的鸭子
}
}
/**
* @author wang 鸭子叫实现类
*/
public class Quack implements Quackable {
@Override
public void quack() {
System.out.println("鸭子呱呱叫");
}
}
/**
* @author wang 叫声的接口
*/
public interface Quackable {
public void quack();
}
/**
* @author wangy 红头鸭
*/
public class ReadHeadDuck extends Duck {
public ReadHeadDuck() {
quackable = new Quack();
flyable = new FlyWithWay();
}
@Override
void exterior() {
System.out.println("我是一只红头鸭");
}
}
/**
* @author wang 橡皮鸭子
*/
public class RubberDuck extends Duck {
public RubberDuck() {
quackable = new Spueak();
flyable = new FlyNoWay();
}
@Override
void exterior() {
System.out.println("橡皮鸭子");
}
}
/**
* @author wang 橡皮鸭叫声实现类
*
*/
public class Spueak implements Quackable {
@Override
public void quack() {
System.out.println("橡皮鸭吱吱叫");
}
}
动态的设定行为让我们来看看使用设计模式的好处
在Duck加入
//我们可以随时调用这两个方法改变鸭子的行为
public void setFlyable(Flyable fa){
flyable = fa;
}
public void Quackable(Quackable q){
quackable = q;
}
新建一个模型鸭
/**
* @author wangy
* 模型鸭
*/
public class ModelDuck extends Duck{
@Override
void exterior() {
System.out.println("我是一只模型鸭");
}
public ModelDuck(){
flyable = new FlyNoWay();//模型鸭不会飞哦
quackable = new Quack();//模型鸭也会叫
}
}
模型鸭一开始是不会飞的’
为模型鸭创造一个动力系统
/**
* @author wangy
* 火箭推动飞行
*/
public class FlyRocket implements Flyable{
@Override
public void Fly() {
System.out.println("火箭动力推动飞行");
}
}
/**
* @author wang
* 测试类
*/
public class test {
public static void main(String[] args) {
Duck md = new ModelDuck();//创建模型鸭对象
md.exterior();
md.PerFormPly();//第一次调用会委托给Flyable对象也就是FlyNoWay实例,该对象是在模型构造器中设置的
md.setFlyable(new FlyRocket());//调用继承来的set方法动态的设置为FlyRocket
md.PerFormPly();
}
}
这样我们就可以随时为鸭子创建方法而不受影响
这样的做法和继承的区别在于鸭子的行为不是继承来的,而是的适当的行为对象组合来的
第三个原则:多用组合,少用继承
不知不觉中我们就使用了策略模式
策略模式定义:定义了算法族,分别封装独立起来,让他们之间可以互相替代,此模式让算法的变化独立于使用算法的客户.