并不是每个自称自己是个OO的人,他就一定能运用好OOP。
普通程序员写的东西好比一把普通钥匙,一把钥匙只能开一个门,而高级程序员就会“造”万能钥匙。
我以前所理解的“简单”就是一把钥匙开一把锁的模式,仅仅只是着眼于解决现在的问题,而设计模式的“复杂”在于它需要造出具有“通用性”的万能钥匙。而我以前写的代码就是前者,这个“简单”不是功能的简单,而是设计的简单。简单的设计意味着缺少灵活性,代码很钢硬,仅仅只是面向过程,一步操作紧挨着下一步。
要使代码可被反复使用,请用’设计模式’对你的代码进行设计.
模拟鸭子程序开始了。
首先来一张类图,这是鸭子程序原始状态
而此时我们需要添加一个新功能:鸭子飞功能(吐槽:明明很多鸭子不会飞)
这很简单,难不倒我们“有OO思想”的人
但这样的设计运用在下面拓展的程序上会变成什么呢?
哇哦,很明显橡皮鸭子不能飞,而在此设计中使用继承(extends)就变得不合理了,
但是我们可以投机取巧一点,变成如下图
这样小聪明的方法,恐怕我自己看的都不能直视了....
很明显对于继承在这个设计当中很几个致命的地方
1:各个鸭子子类继承父类时,使得代码在多个子类中重复
2:没有拓展性,对于改变会费劲
3:往往改变一点地方很多地方就都要跟着改变,找出其他鸭子类中不想要的改变,牵一发而动全身。
设计原则一:找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起
注:当每次有新的需求的时候,就会使某方面发生变化,那么你就可以确定,这部分的代码就需要被抽出来,和其他稳定的代码有所区分,这样使得代码变化引起的不经意后果变少,系统变得更加富有弹性。
设计原则二:针对接口编程,而不是针对实现编程。
注:从现在开始我们编写的代码要富有弹性,让其运行时能“动态”的改变飞行或者呱呱叫行为
进行一点小改变
这里将飞行和叫的行为各自“抽象”出来成接口,这两个行为被分开成两个类,这两个类专门为提供某行为的实现提供接口。不同与之前设计:行为来自Duck父类中子类的具体实现,或是继承某个接口并由子类自行实现而来。这两种做法都依赖“实现”。而此时子类将使用接口所代表的行为,所以实际的“实现”不会被绑死在鸭子的子类中。(特定的具体行为编写在实现了FlyBehavior和QuackBehavior接口的实现类中)
这里放出具体行为设计
注:此时飞行和叫的动作(接口)可以被其他对象复用,此时这两个行为和鸭子类(Duck)无关了,而且往后加入新的行为也不怕了大量修改代码了。
鸭子父类:
package Entity;
import Interface.FlyBehavior;
import Interface.QuackBehavior;
//鸭子父类
public class Duck {
// 添加两个接口变量
FlyBehavior flyBehavior;// 每只鸭子都会引用完成FlyBehavior接口的对象
QuackBehavior quackBehavior;// 每只鸭子都会引用完成QuackBehavior接口的对象
public FlyBehavior getFlyBehavior() {
return flyBehavior;
}
public void setFlyBehavior(FlyBehavior flyBehavior) {
this.flyBehavior = flyBehavior;
}
public QuackBehavior getQuackBehavior() {
return quackBehavior;
}
public void setQuackBehavior(QuackBehavior quackBehavior) {
this.quackBehavior = quackBehavior;
}
public Duck() {
}
public void swim() {
System.out.println("鸭子游泳。。。。。。");
}
// 动作
public void display() {
System.out.println("每只鸭子都会动");
}
// 这些方法取代fly(),quack()
// 鸭子对象不亲自处理呱呱呱叫行为,而是委托给quackBehavior引用对象
public void performQuack() {
quackBehavior.quack();
}
// 鸭子对象不亲自处理飞行为,而是委托给flyBehavior引用对象
public void performFly() {
flyBehavior.fly();
};
}
飞行和叫动作接口
package Interface;
//飞行接口
public interface FlyBehavior {
public void fly();
}
package Interface;
//鸭子叫接口
public interface QuackBehavior {
public void quack();
}
//绿头鸭和橡皮鸭类
package Entity;
import Implements.FlyWithWings;
import Implements.Quack;
//绿头鸭子类
public class MallardDuck extends Duck {
@Override
public void display() {
System.out.println("绿头鸭子");
}
/* 当MallardDuck被实例化时,
* 构造器会把继承来的quackBehavior,flyBehavior
* 实例变量初始化成Quack,FlyWithWings的类型
* Quack,FlyWithWings分别是接口的实现类
* */
public MallardDuck() {
quackBehavior = new Quack();
flyBehavior = new FlyWithWings();
}
}
package Entity;
import Implements.FlyNoWay;
import Implements.Squeak;
public class RubberDuck extends Duck {
//橡皮鸭类
@Override
public void display() {
System.out.println("我是一只橡皮鸭");
}
public RubberDuck() {
quackBehavior = new Squeak();
flyBehavior = new FlyNoWay();
}
}
各个行为的具体实现类,都实现了接口
package Implements;
import Interface.FlyBehavior;
public class FlyNoWay implements FlyBehavior {
@Override
public void fly() {
System.out.println("不会飞");
}
}
package Implements;
import Interface.FlyBehavior;
public class FlyWithWings implements FlyBehavior {
@Override
public void fly() {
System.out.println("鸭子飞起来了。。。。。。");
}
}
package Implements;
import Interface.QuackBehavior;
public class MuteQuack implements QuackBehavior {
@Override
public void quack() {
System.out.println("不会叫");
}
}
package Implements;
import Interface.QuackBehavior;
public class Quack implements QuackBehavior {
@Override
public void quack() {
System.out.println("鸭子呱呱呱叫。。。。。。");
}
}
package Implements;
import Interface.QuackBehavior;
public class Squeak implements QuackBehavior {
@Override
public void quack() {
System.out.println("鸭子吱吱吱叫。。。。。。");
}
}
测试类
package TestMain;
import Entity.Duck;
import Entity.MallardDuck;
import Entity.RubberDuck;
public class TestMain {
public static void main(String[] args) {
//这里使用多态,让Duck知道我们要创建一只绿头鸭
Duck d = new MallardDuck();
//重载方法
d.display();
d.performQuack();
d.performFly();
System.out.print("\n");
Duck a=new RubberDuck();
a.display();
a.performQuack();
a.performFly();
}
}
效果:
设计原则三:多用组合,少用继承。
注:使用组合建立系统具有很大的弹性,不仅可将算法封装成类,更可以“动态的改变行为”,只要组合对象符合正确的接口标准即可。
此时我刚刚写的例子基于一个策略模式思想写的。当然你可能会问:“用这个设计模式到底有什么用呢?这样功能我也能实现了,干嘛要搞的那么复杂?”。对此我不能用专业的思想去回答这样的疑问,我只能说随着自己学习的不断深入往往很多以前自认为对的东西,其实是不正确的。
策略模式要点:
1:良好的OO设计必须具备可复用性、可扩充性、可维护性三个特征
2:模式不是代码,而是针对设计问题的通用解决方案。
3:大多数的模式都允许系统局部改变独立于其他部分。
4:记着把系统中会改变的部分抽出来封装
5:模式会让开发人员之间有共同的语言,而且会让自己少走很多坑
这里我想说一点,很多人都觉得设计模式跟算法一样都是可有可无的东西,我觉得用设计模式是着眼于未来,可以提高系统的扩展性,减少重复劳动,虽然有的时候可能会增加工作量,但比起后期无止境的维护,这点反而不算什么,而设计模式是不会提高系统运行速度的,但我认为设计模式非常重要,包括带我的前辈程序员都教导我脑中要有模式概念,当然这个要因人而异。
感谢你看到这里,策略模式部分结束,本人文笔随便,若有不足或错误之处望给予指点,90度弯腰~很快我会发布下一个设计模式内容,生命不息,编程不止!
参考书籍:《Head First 设计模式》
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。