設計模式(1)—策略模式
0.什麼是設計模式?
我們經常使用被人設計好的庫和框架,利用他們的API(Application Programming Interface)編寫我們的程式,但是我們得把它們“組合”起來。像小孩搭積木一樣,建成自己的大廈。其中你用到的“方式”,都是事先存在你腦海中的,這些“方式”都是抽象的,並不是一塊塊具體的積木。並且,你在搭建的過程中,希望搭建成的玩具簡潔,美觀,安全,可擴充套件性強…等等,都是基於你腦海中的“方式”而完成的。這些“方式”.是你在搭建過程中,所經歷的成功,失敗和借鑑別人的大廈得來的。這些“方式”就稱為設計模式。
設計模式比庫的等級更高,它將告訴我們如何組織類和物件解決問題。當然,在java優秀的類和庫中,也用到了設計模式,這就是原始碼優秀的原因。這些優秀的設計模式被記錄下來,從而知道我們的思想。
為什麼一再強調腦海中呢? 當然需要,首先你的腦海中要有基本的模式,才能在實現過程中寫出好的設計。
1.什麼是策略模式?
OO即(Object Oriented)面向物件。瞭解過高階語言的同學都知道,OOP(Object Oriented Programming)即面向物件程式設計,是重頭戲。它的特徵包括抽象,封裝,繼承,多型。
策略模式——定義演算法族,分別封裝起來,讓它們之間可以互相獨立替換,此模式讓演算法的變化獨立於使用演算法的客戶。
乍一看非常晦澀難懂,待我們舉一個小栗子,大家就會明白了
首先我們要了解幾個必要的原則:
(0)封裝變化:找出程式中會變化的方面,然後將其和固定不變的方面相分離。
(1)多用組合(combination),少用繼承
(2)針對介面程式設計,不針對實現程式設計。
這3個原則非常管用,將貫穿我們整個設計模式的學習過程當中。
下面開始最精彩的部分,你將體驗到設計模式的魅力。
先從簡單的一個實現開始,公司要求你做一個鴨子系統。鴨子我們都知道,一邊游泳,一邊呱呱叫。同時按特性還分為各種的鴨子,比如紅頭鴨子,綠頭鴨子。按材料分:真鴨子,橡皮鴨(不會飛,吱吱叫),木頭鴨子(不會飛,不會叫)
這並不難
現在我們要讓鴨子會飛 ,“理所當然”我們將在鴨子類中新增程式碼:
:
繼承了鴨子類的橡皮鴨有個小功能“吱吱叫”(這不難,複寫父類即可),但是隨之而來的問題出現了:橡皮鴨也飛了起來!
同時,如果有木頭鴨子。繼承了鴨子類的木頭鴨也具備了 “飛”和“叫”的功能!這是完全錯誤的。
作為一個優秀的OO程式猿,這也不難:我們繼續複寫!
當有了第四種鴨子(不會飛,會叫),複寫!
第五種鴨子(會飛,不會叫),複寫!
第六種…第七種….
這樣寫至少會帶來幾個問題:
A.程式碼在多個子類中重複
B.改變父類,造成其他鴨子不要的改變(比如木鴨子不要飛)
所以我們帶來了一個原則來拯救你:分開變化和不會變化的部分:
我們在利用第二個原則:針對介面程式設計
如:我們用Flyable和Quackable介面
緊接著我們實現這兩個介面:
現在,父類鴨子將飛行和叫動作“委託”(delegate)別人處理,而不是在父類中定義Duck的方法,在父類中,我們不關心叫介面的物件到底是什麼,我們只關心
該物件知道如何實現叫就夠了。
好了,講到這裡,我們來寫一寫程式碼:
//鴨子父類
public abstract class Duck {
FlyBehavior flyBehavior;
QuackBehavior quackBehavior;
public Duck(){
}
public abstract void display();
public void performFly(){
flyBehavior.fly();
}
public void performQuack(){
quackBehavior.quack();
}
public void swim() {
System.out.println("All duck float,even ddecoys!");
}
}
//定義FlyBehavior 介面
public interface FlyBehavior{
public void fly();
}
//實現FlyBehavior介面
public class FlyNoWay implements FlyBehavior {
public void fly() {
System.out.println("I cannot fly");
}
}
public class FlyWithWings implements FlyBehavior {
public void fly() {
System.out.println("I am flying!");
}
}
//定義QuackBehavior介面
public interface QuackBehavior {
public void quack();
}
//實現QuackBehavior介面
public class Quack implements QuackBehavior {
public void quack() {
System.out.println("Quack");
}
}
public class MuteQuack implements QuackBehavior{
public void quack() {
System.out.println("<<Silence>>");
}
}
public class Squeak implements QuackBehavior{
public void quack() {
System.out.println("Squak");
}
}
//公司讓你建立的鴨子
public class MallardDuck extends Duck{
public MallardDuck () {
quackBehavior = new Quack();
flyBehavior = new FlyWithWings();
}
public void display(){
System.out.println("I am a real Mallard duck");
}
}
//測試單元
public class MiniDuckSimulator{
public static void main(String[] arg) {
Duck mallard = new MallardDuck();
mallard.performQuack();
mallard.performFly();
}
}
//輸出結果
Quack
I am flying!
另外,我們還可以動態設定行為,在鴨子執行時的行為也可以改變。這也是最開始的繼承方法中所沒有的。
在Duck類中,加入兩個新方法:
public void setFlyBehavior(FlyBehavior fb)
{
flyBehavior = fb;
}
public void setQuackBehavior(QuackBehavior qb)
{
qucakBehavior = qb;
}
// 新建一個鴨子
public class MallardDuck extends Duck{
public MallardDuck () {
quackBehavior = new Quack();
flyBehavior = new FlyWithWings();
}
public void display(){
System.out.println("I am a model duck");
}
}
//實現FlyBehavior介面
public class FlyRocketPowered implements FlyBehavior{
public void fly(){
System.out.println("i am flying with a rocket!");
}
}
//測試單元
public class MiniDuckSimulator{
public static void main(String[] arg) {
Duck model= new ModelDuck();
model.performFly();
model.setFlyBehavior(new FlyRocketPowered());
model.performFly();
}
}
//執行結果
I cannot fly
i am flying with a rocket!