【问题标题】:Design OOP question on Decorator and Strategy pattern C#关于装饰器和策略模式 C# 的设计 OOP 问题
【发布时间】:2010-10-07 07:03:23
【问题描述】:

比如说你有一个基础抽象类

public abstract Foo
{
  IFlyable _fly;
  ISwimmable _swim;

  void performSwim()
  {
    _swim.swim();
  }

 void performFly()
 {
   _fly.fly();
 }
}

并拥有系统中的行为/算法

interface IFlyable { void fly(); }
interface ISwimmable { void swim(); }
interface IVoteable { void vote(); }

等等等等

现在你有多个实现它的类,具体包括 IFlyable、ISwimmable 等

class Bar: Foo { _swim = new ConcerteSwim(); }
class Baz: Foo { _fly = new ConcreteFly(); }

等等等等

一种是使用 Foo 基类中的策略模式来交换行为。

我们也可以使用装饰器模式来包装它,但由于装饰器用基类包装它,如果我们稍后添加更多行为,我们如何真正允许打开关闭原则工作而不触及基类对象。由于这些行为可能会随着我们添加更多而具有不同的签名,而不仅仅是例如调用装饰器

void performSwim()
{
     swimWithMoreSpeed() + foo.performSwim()
}

我想我的问题是如果我添加更多行为,我怎么能不修改基类并且仍然能够说添加 IWeaponBehavior、ISomeBehaviour 到一个类。

例如我想上课

public class XYF: Foo
{

}

但我想给它一些 ISomeBehaviour 的行为,有没有一种方法可以说用这些行为包装它,或者更像是一个 ConcreteFoo 用这些行为包装它,现在做一些事情而不是实现接口在具体 xyz 上,虽然这会让你实现这么多种具体行为类,如游泳行为、空行为等,但没有办法摆脱它。

在设计模式中有没有办法做到这一点?它几乎看起来像是一种混合模式。

我知道,如果它像鸭子一样走路和像鸭子一样嘎嘎叫,但需要电池,那么你的抽象就有问题了。

希望这是有道理的。

【问题讨论】:

  • Arg!骆驼案例的方法?成员的下划线前缀!?!是的,这是一个无益的评论,但我就是忍不住;)

标签: c# oop design-patterns decorator strategy-pattern


【解决方案1】:

试试装饰器模式。要支持新的行为,您只需实现一个继承自Foo 的新类,实现IWeaponBehavior,并装饰另一个Foo 对象。然后,您现在可以将WeaponDecorator 用作FooIWeaponBehavior,并且仍然可以访问它所装饰的基础Foo

基类:

public abstract Foo { void Jump(); }

具有基本行为的具体子类:

public Bar : Foo { void Jump() { /* jump! */ } }
public Baz : Foo { }

行为接口:

interface IFlyable { void Fly(); }
interface ISwimmable { void Swim(); }

具有行为的装饰器子类:

public FlyableFoo : Foo, IFlyable
{
    public Foo Base { get; set; }
    public FlyableFoo(Foo base) { Base = base; }
    void Fly() { Base.Jump(); /* fly! */ }
}

现在我们可以使用 Jump() 的特定实现让任何 Foo 飞行:

Baz baz = new Baz();
FlyableFoo flybaz = new FlyableFoo(baz);
flybaz.Fly();

【讨论】:

  • 你能举个例子吗?
【解决方案2】:

访客模式怎么样?它允许基类的实现者改变行为,而无需强制基类针对每个新实现进行更改:

public interface IFlyable { void fly(); }
public interface ISwimmable { void swim(); }
public interface IVoteable { void vote(); }

public abstract class Animal
{
    public abstract void Accept(AnimalVisiter Visitor);
    //some common behaviour is here
    public bool LegsKicking { get; set; }
    public bool ArmsFlapping { get; set; }
}

//This class now absorbs new responisbilities, so base class doesn't have to
public class AnimalVisiter
{
    public void Visit(ISwimmable Subject)
    {
        Subject.swim();
    }

    public void Visit(IVoteable Subject)
    {
        Subject.vote();
    }

    public void Visit(IFlyable Subject)
    {
        Subject.fly();
    }
}

public class SwimmingHuman : Animal, ISwimmable
{
    public void swim()
    {
        LegsKicking = true;
    }

    public override void Accept(AnimalVisiter Visitor)
    {
        Visitor.Visit(this);
    }
}

public class VotingHuman : Animal, IVoteable
{

    public override void Accept(AnimalVisiter Visitor)
    {
        Visitor.Visit(this);
    }

    public void vote()
    {
        VoteCast = true;
    }
    //some specific behaviour goes here
    public bool VoteCast { get; set; }
}

public class SwimmingTiger : Animal, ISwimmable
{

    public void swim()
    {
        LegsKicking = true;
        //also wag tail, flap ears
    }

    public override void Accept(AnimalVisiter Visitor)
    {
        Visitor.Visit(this);
    }
}

【讨论】:

    【解决方案3】:

    所以这是我的看法:

    public enum ActionType
    {
       Swim, Vote, Fly
    };
    
    public interface IBehavior
    {
       public boolean ActionReady;
       public ActionType _type;
    
       public void performMyAction();
    }
    
    public abstract Foo
    {
      IBevahior[] behaviors;
    
      // if you want to keep track of behavior states as on or off
      void perform()
      {
        for(int i = 0; i< behaviors.length; i++)
        {
           if(behaviors[i].ActionReady)
           {
              behaviors[i].performMyAction();
           }
        }
      }
    
      // if you want to call behaviors individually
      void performType(ActionType type)  // however you want to make the distinction
      {
         for(int i = 0; i < behaviors.length; i++)
         {
            if(behaviors[i].type = type)
            {
                behaviors[i].performMyAction();
            }
         }
      }
    }
    

    然后让您的接口(如 ISwimmable 等)从 IBehavior 继承。

    【讨论】:

    • 但是我们会进入一个巨大的 switch 类型的语句,每个语句可能有不同的 performMyAction。
    • 在这种情况下,子类有可能在 performMyAction() 调用中使用的潜在参数的成员变量是有意义的。不是最漂亮的,但它非常灵活。 myFoo.performType(ActionType.Swim);
    猜你喜欢
    • 1970-01-01
    • 2013-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-12
    • 1970-01-01
    • 1970-01-01
    • 2010-12-16
    相关资源
    最近更新 更多