zoukankan      html  css  js  c++  java
  • OOAD之策略模式(1)

    在引入策略模式之前。先用一个小项目解释为什么要有策略模式。这是一个模拟鸭子的小游戏。最开始需求为,所有的鸭子都会叫以及都会游泳且叫声游泳姿势都一样。因此我们用原始OO的思维去编写代码时很容易做到

    public abstract class Duck{
      public void yuck(){
            System.out.println("嘎嘎叫");
        }
    
       public void swim(){
            Systrm.out.println("i am swim");
        }              
    
            public abstract void display();
    }

    也就是封装一个超类。然后设置两个子类通用的方法,叫和游泳。此时再编写程序需要的各种不同的鸭子子类。他们都能够叫和游泳。此时编写一个测试类。

    public class Test{
    
       public static void main(String[] args){
           Duck gDuck = new GreenDuck();
           Duck rDuck  = new RedDuck();
           
           gDuck.display();
           gDuck.yuck();
           gDuck.swim();
    
           rDuck.display();
           rDuck.yuck();
           rDuck.swim();
        
        }  
    }
    public class GreenHeadDuck extends Duck{
    
       @override
           public void display(){
            System.out.println("绿头鸭");
        }
    }    
    public class RedHeadDuck extends Duck{
        
        @override
            public void display(){
            System.out.println("红头鸭");
        }
    }    

    测试类的结果为:绿头鸭   嘎嘎叫  i am swim    红头鸭  嘎嘎叫  i am swim。这个完全没有问题。好的,现在游戏上线,市场火热,新需求来了。鸭子要会飞。那我们继续用OO的思维去想,

    那么可以给超类Duck中设置一个通用的fly方法。让所有的鸭子都会飞。让我们把Duck的代码改一下。改成下面的

    public abstract class Duck{
      public void yuck(){
            System.out.println("嘎嘎叫");
        }
    
       public void swim(){
            Systrm.out.println("i am swim");
        }              
    
            public abstract void display();
    
         public void fly(){
          System.out.println("i am flying");
        }
    }

    好的,现在的确实现了鸭子会飞的功能,但是这也引起了一些问题。因为事实上不是所有的鸭子都会飞。你在超类中定义了方法。影响的是全部的子类。容易导致溢出的问题,也就是。有些子类不需要。但是你给它强制加了。好,那如果现在有些子类不会飞。我们继续用OO的思维去解决。那么可以在不会飞的子类中用重写去覆盖掉父类给他的方法。我们加入绿头鸭不会飞,我们修改以下绿头鸭的代码如下

    public class GreenHeadDuck extends Duck{
    
       @override
           public void display(){
            System.out.println("绿头鸭");
        }
    
        @override
           public void fly(){
           System.out.println("i can't fly")
        }
    }    

    好,可以通过在子类中覆盖来实现有些鸭子会费飞有些不会飞这个需求。但是在我们这个例子中鸭子种类少,这么玩没有关系。但如果由2000种鸭子呢。难道你要一个一个的去覆盖子类中的方法。

    很明显就能看出单纯的面向对象,也就是OO的思想在设计这个需求的时候并不完善。那也有些人说,那我不在超类Duck中定义fly的方法。我在子类中去定义,谁会飞我就给谁定义。这其实和上面一样。如果子类基数大。那么你写的重复代码也会很多。那么OO中的代码复用就完全没有体现出来了。

    因此,我们分析以下上面OO设计的问题所在。为什么会说这个设计不好。原因是在于它的行为和客户类没有分离。也就是“干什么” 和 “怎么干”没有分离开来。例如上面的例子。客户类中的fly是干什么,但是怎么干,fly里面怎么定义代码,他们没有分离。全部堆在客户类中,耦合度太高。导致,你的“怎么干“放在客户类中定义,无论你放在超类还是子类。都容易出问题,因此就导出了策略模式这个概念

    策略模式是干嘛的,是去耦合的。它所做的就是把”干什么“和”怎么干“分离开来。把”怎么干“封装到一个策略接口或者抽象类中。然后编写一系列的怎么干算法封装起来。客户类中只需要调用这个算法就ok了

    策略模式的两种标准点的定义:

    图视:

        环境对象(Context):该类中实现了对抽象策略中定义的接口或者抽象类的引用。
          抽象策略对象(Strategy):它可由接口或抽象类来实现。
          具体策略对象(ConcreteStrategy):它封装了实现同不功能的不同算法。

    策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换,策略模式让算法独立于使用它的客户而独立变化。

    使这些算法在客户端调用它们的时候能够互不影响地变化。


    策略模式:分别封装行为接口,实现算法族,超类中放行为接口对象。在子类中的构造方法中去实例化行为对象。原则就是:分离变化部分,封装接口,基于接口编程各种功能族,让行为算法的变化独立于算法的使用者。

    使用策略模式的步骤:

    1.分析变化的行为,拿出来进行封装成一个策略接口或者抽象类。然后定义该行为的多种实现算法。形成算法族

    2.在客户超类中放策略接口。

    3.在子类的构造方法中去具体实例化具体的策略算法

     


    下面让我们看看用策略模式修改模拟鸭子的这个项目。

    1.封装变化的部分。当前鸭子的变化行为为fly。有些会飞,有些不会飞。有些会飞的好,有些飞的不好。因此封装fly这个行为为一个策略接口。FlyObject

    public interface FlyObject{
       void fly();
    }

    接下来,编写项目中实现fly这个行为的多种算法。例如不会飞 nofly,飞的好,good fly,飞得不好 badfly。这三个行为族

    public class NoFly implements FlyObject{
        
       @override
        public void fly(){
           System.out.println("不会飞")
        }
    }
    public class GoodFly implements FlyObject{
    
        @override
           public void fly(){
            System.out.println("Good fly");
        }
    }            
    public class BadFly implements FlyObject{
    
       @override
         public void fly(){
           System.out.println("bad fly");
        }
    }




    之后在客户类的超类也就是Duck中放置策略接口,也就是FlyObject。

    package com.lyh.strategy.duck;
    
    import com.lyh.strategy.flybehavior.FlyBehavior;
    import com.lyh.strategy.yuckbehavior.YuckBehavior;
    
    public abstract class Duck {
    
        FlyBehavior flybehavior;
        YuckBehavior yuckbehavior;
        public Duck() {
            
        };
        
        public void fly() {
            flybehavior.fly();
        }
        
        public void yuck() {
            yuckbehavior.yuck();
        }
        
        public abstract void display();
        
        public void setFlyBehavior(FlyBehavior fb) {
            this.flybehavior = fb;
        }
        
        public void setYuckBehavior(YuckBehavior yb) {
            this.yuckbehavior = yb;
        }
        
        //这个方法子类通用,不会变化
        public void swim() {
            System.out.println("i am swim");
        }
    }
    
    
    
    在客户类的子类中去实例化具体的策略接口算法。

    package com.lyh.strategy.duck;
    
    import com.lyh.strategy.flybehavior.GoodFlyBehavior;
    import com.lyh.strategy.yuckbehavior.GaGaYuckBehavior;
    
    public class GreenHeadDuck extends Duck {
    
        public GreenHeadDuck() {
            flybehavior = new GoodFlyBehavior();
            yuckbehavior = new GaGaYuckBehavior();
        }
        @Override
        public void display() {
            System.out.println("绿头鸭");
        }
    
    }
    
    
    package com.lyh.strategy.duck;
    
    import com.lyh.strategy.flybehavior.NoFlyBehavior;
    import com.lyh.strategy.yuckbehavior.NoYuckBehavior;
    
    public class RedHeadDuck extends Duck {
    
        
        public RedHeadDuck() {
            flybehavior = new NoFlyBehavior();
            yuckbehavior = new NoYuckBehavior();
        }
        @Override
        public void display() {
            System.out.println("红头鸭");
        }
    
        
    }
    因此,通过这种方式,能够实现客户类中的行为和客户类相分离。把变化的行为独立出来。抽象成接口。这样如果有新的行为需求,可以直接在行为族中加入。如果有新的客户类对象。也可以让他们
    具体选择的去实现行为族。达到降低耦合度的目的。


    但是,策略模式也有缺点。也就是客户类必须了解策略行为族中的每一个算法的实现以及原理。只有全部知晓。客户类才能去区别调用他们而不会出错。这时策略模式必须要做到的一点。

    我们什么时候使用策略模式:这个没有具体的需求。一般而言。如果你定义的抽象父类中的一些方法是可变的。子类中不一定需要这些方法的时候。建议用策略模式将变化的行为封装起来。这其实也是一种方法的封装。你将行为封装,那么其他的类也可以去使用。

  • 相关阅读:
    java第十三周作业
    java第十三周随堂
    安卓作业
    5.29 第十三周作业
    5.28第十三周上机练习
    5.22第十二周作业
    5.21第十二章上机练习
    5.15 第十一周作业
    5.14 第十一周 上机练习
    5.7第十周上机练习
  • 原文地址:https://www.cnblogs.com/exceptionblog/p/8372941.html
Copyright © 2011-2022 走看看