什么是策略模式?
先看栗子!!!
假設(shè)有一個(gè)抽象超類Duck ,其代碼如下;
<pre>
public abstract class Duck {
public void Quack(){
System.out.println("--Duck speark--");
}
public abstract void display();
public void swim(){
System.out.println("--Duck swim--");
}
}
</pre>
假設(shè)其有一個(gè)GreenDuck子類,代碼如下:
<pre>
public class GreenDuck extends Duck {
public void display() {
System.out.println("****鴨子頭發(fā)色是綠色****");
}
}
</pre>
實(shí)例化對(duì)象
<pre>
public static void main(String[] args) {
GreenDuck gd=new GreenDuck();
RedDuck rd=new RedDuck();
//綠頭鴨
gd.display();
gd.Quack();
gd.swim();
//紅頭鴨
rd.display();
rd.Quack();
rd.swim();
}
</pre>
運(yùn)行結(jié)果
<pre>
****鴨子頭發(fā)色是綠色的****
--Duck speark--
--Duck swim--
****鴨子頭發(fā)色是紅色的****
--Duck speark--
--Duck swim--
</pre>
以上使用了繼承的方法,實(shí)現(xiàn)了代碼的復(fù)用性。使得子類中代碼更簡(jiǎn)潔。但是一項(xiàng)新的問(wèn)題隨之而來(lái)。
假設(shè)超類中擴(kuò)展了一個(gè)新的需求,例如:
<pre>
public abstract class Duck{
...;
public void fly(){
System.out.println("--Duck fly--");
}
}
</pre>
但是超類中的這個(gè)fly()方法不一定是其他子類所需要的。
雖然可以使用面向?qū)ο?OO)的思維,通過(guò)覆蓋父類方法解決繼承的問(wèn)題,代碼如下:
<pre>
public class GreenHeadDuck extend Duck{
...
public void fly(){
System.out.println("~GreenHeadDuck no fly~");
}
}
</pre>
又假設(shè)新出現(xiàn)一個(gè)新的子類Toy_Duck(玩具鴨),這個(gè)子類不會(huì)父類的fly(),Quack()方法,則在Toy_Duck中又要覆蓋相應(yīng)的方法。因此,子類都要從繼承父類中的方法中進(jìn)行篩選有用的功能(又稱覆蓋父類相應(yīng)方法),增加了子類的工作量,可得出復(fù)雜度O(N^2)。
為了應(yīng)對(duì)項(xiàng)目的擴(kuò)展,重新考慮設(shè)計(jì)方式,避免改動(dòng)源碼,降低復(fù)雜度
1)分析項(xiàng)目變化與不變部分,提取變化部分,抽象成接口+實(shí)現(xiàn);
2)鴨子哪些功能是會(huì)根據(jù)新需求變化的?叫聲、飛行..
1、接口:
<pre>
//飛行接口
1) public interface FlyBehavior
{
void fly();
}
//叫聲接口
2)public interface QuackBehavior
{
void quack();
}
</pre>
好處:新增行為簡(jiǎn)單,行為類更好的復(fù)用,組合更方便。既有繼承帶來(lái)的復(fù)用好處,沒(méi)有挖坑
上述接口的實(shí)現(xiàn)類有GoodFlyBehavior、BadFlyBehavior、NoFlyBehavior和GoodQuackBehavior、 BadQuackBehavior、NoQuackBehavior
飛行行為對(duì)象族代碼如下:
<pre>
//GoodFlyBehavior行為對(duì)象
public class GoodFlyBehavior implements FlyBehavior {
public void fly() {
System.out.println("--GoodFly--");
}
}
//BadFlyBehavior行為對(duì)象
public class BadFlyBehavior implements FlyBehavior {
public void fly() {
System.out.println("--BadFly--");
}
}
//NoFlyBehavior行為對(duì)象
public class NoFlyBehavior implements FlyBehavior {
public void fly() {
System.out.println("--不會(huì)飛--");
}
}
</pre>
叫聲行為對(duì)象族代碼如下:
<pre>
//GoodQuackBehavior行為對(duì)象
public class GoodQuackBehavior implements QuackBehavior {
public void quack() {
System.out.println("好聽的叫聲");
}
}
//BadQuackBehavior行為對(duì)象
public class BadQuackBehavior implements QuackBehavior {
public void quack() {
System.out.println("難聽的叫聲" );
}
}
//NoQuackBehavior行為對(duì)象
public class NoQuackBehavior implements QuackBehavior {
public void quack() {
System.out.println(" 不會(huì)叫");
}
}
//
</pre>
重新設(shè)計(jì)模擬鴨子項(xiàng)目:
<pre>public abstract class Duck {
//將飛行、叫聲行為方法定義為對(duì)象。
FlyBehavior flyBehavior;
QuackBehavior quackBehavior;
//空構(gòu)造方法
public Duck(){
}
public void Fly(){
flyBehavior.fly();
}
public void Quack(){
quackBehavior.quack();
}
abstract void display();
//設(shè)置叫聲行為對(duì)象接口,便于類方法的組合
public void SetQuackBehavoir(QuackBehavior qb) {
quackBehavior = qb;
}
//設(shè)置飛行行為對(duì)象接口,便于類方法的組合
public void SetFlyBehavoir(FlyBehavior fb) {
flyBehavior = fb;
}
public void swim() {
System.out.println("swimming");
}
}
</pre>
Duck子類GreenHeadDuck代碼如下:
<pre>
public class GreenHeadDuck extends Duck {
public GreenHeadDuck(){
flyBehavior = new GoodFlyBehavior();
quackBehavior = new GoodQuackBehavior();
}
void display() {
System.out.println("GreenHead(綠頭鴨子)");
}
}
</pre>
Duck子類 RedHeadDuck代碼如下:
<pre>
public class RedHeadDuck extends Duck {
public RedHeadDuck() {
flyBehavior = new BadFlyBehavior();
quackBehavior = new BadQuackBehavior();
}
void display() {
System.out.println("RedHead(紅頭鴨子)");
}
}
</pre>
于是我們創(chuàng)建一個(gè)類去檢驗(yàn)以上代碼的可行性。
<pre>
public class TestDemo {
public static void main(String[] args) {
Duck mGreenHeadDuck = new GreenHeadDuck();
Duck mRedHeadDuck = new RedHeadDuck();
//對(duì)象的屬性和方法
mGreenHeadDuck.display();
mGreenHeadDuck.Fly();
mGreenHeadDuck.Quack();
mGreenHeadDuck.swim();
System.out.println("---------分割線----------");
mRedHeadDuck.display();
mRedHeadDuck.Fly();
mRedHeadDuck.Quack();
mRedHeadDuck.swim();
mRedHeadDuck.display();
System.out.println("---------分割線----------");
//可以根據(jù)子類自身屬性設(shè)置相應(yīng)的參數(shù),解決了因繼承帶來(lái)的某些問(wèn)題。
mRedHeadDuck.SetFlyBehavoir(new NoFlyBehavior());
mRedHeadDuck.display();
mRedHeadDuck.Fly();
mRedHeadDuck.SetQuackBehavoir(new NoQuackBehavior());
mRedHeadDuck.Quack();
System.out.println("---------分割線----------");
mGreenHeadDuck.SetFlyBehavoir(new BadFlyBehavior());
mGreenHeadDuck.display();
mGreenHeadDuck.Fly();
mGreenHeadDuck.SetQuackBehavoir(new NoQuackBehavior());
mGreenHeadDuck.Quack();
}
}
</pre>
運(yùn)行結(jié)果:
<pre>
GreenHead(綠頭鴨子)
--GoodFly--
好聽的叫聲
im swim
---------分割線----------
RedHead(紅頭鴨子)
--BadFly--
難聽的叫聲
i m swim
RedHead(紅頭鴨子)
---------分割線----------
RedHead(紅頭鴨子)
--不會(huì)飛--
不會(huì)叫
---------分割線----------
GreenHead(綠頭鴨子)
--BadFly--
不會(huì)叫
</pre>
總結(jié):
策略模式:將類中的行為抽離成接口,實(shí)現(xiàn)相應(yīng)的算法族,在超類里放置行為接口對(duì)象,并提供設(shè)置接口,提高行為組合能力,在子類中具體設(shè)定行為對(duì)象。原則就是:分離變化部分,封裝接口,基于接口編程各種功能。此模式讓行為算法的變化獨(dú)立于算法的使用者。
注意點(diǎn):
策略模式的思想注意點(diǎn)在于:
(1、分析項(xiàng)目中變化部分與不變部分
(2、多用組合少用繼承;用行為類組合,而不是行為的繼承。更有彈性
(3、設(shè)計(jì)模式有沒(méi)有相應(yīng)的庫(kù)直接使用?有些庫(kù)或框架本身就用某種設(shè)計(jì)模式設(shè)計(jì)的
(4、如果找不到適用的模式怎么辦
附言
作為一個(gè)Java程序員,在開發(fā)中遇到的問(wèn)題,多使用面向?qū)ο?,基本原則,抽象設(shè)計(jì)超類繼承的做法去解決。如果還不能解決?那就自己看著吧!