如何重构方法调用哪个看起来一样?

时间:2011-12-28 15:44:30

标签: c# design-patterns refactoring

我有不同的水果类,都实现了相同的界面IFruit

public interface IApple : IFruit{ }  
public interface IBanana : IFruit{ }  
public interface ICarrot: IFruit{ }  

他们每个人都有自己的抽屉:

public class AppleDrawer
{
    public void Draw(IApple apple, Graphics graphics){}
}

public class BananaDrawer
{
    public void Draw(IBanana banana, Graphics graphics){}
}

如果我想画一份水果清单,我正在做以下

public void DrawFruits(List<IFruit> fruits, Graphics graphics)
{
    foreach(var fruit in fruits)
    {
        if(fruit is IBanana)
        {
            var banana = (IBanana)fruit;
            var drawer = new BananaDrawer();
            drawer.Draw(banana, graphics);
        }
        else if(fruit is IApple)
        {
            var apple = (IApple)fruit;
            var drawer = new AppleDrawer();
            drawer.Draw(banana, graphics);
        }
        etc...

}

当我阅读我的代码时,我觉得非常脏 我的问题是多个if..else语句,因为我有12个不同的水果,我必须在我当前的项目中做很多这样的陈述。

有没有办法重构我的DrawFruits方法?
我正在考虑一种工厂模式,但我真的不知道该怎么做。
我的水果类是否必须将抽屉作为财产?或者我可以调用抽屉工厂方法吗?

这是我在当前项目中发现很多的模式,我找不到满足我的解决方案。

4 个答案:

答案 0 :(得分:5)

一种方法是在GetDrawer

上设置IFruit
public interface IFruit 
{
    BaseDrawer GetDrawer();
}

BaseDrawer界面

public interface BaseDrawer
{
    void Draw(IFruit fruit, Graphics graphics);
}.

public class AppleDrawer : BaseDrawer
{
    public void Draw(IFruit apple, Graphics graphics) { }
}

public class BananaDrawer : BaseDrawer
{
    public void Draw(IFruit banana, Graphics graphics) { }
}

现在你的水果简单

   public void DrawFruits(List<IFruit> fruits, Graphics graphics)
    {
        foreach (var fruit in fruits)
        {
            var drawer = fruit.GetDrawer();
            drawer.Draw(fruit, graphics);
        }
    }

有时您需要DrawerPlotterPrinter,因此您的IFruit可能会过于沉重,如下所示

public interface IFruit 
{
    BaseDrawer GetDrawer();
    BasePrinter GetPrinter();
    BasePlotter GetPlotter();
}

Visitor pattern is a good solution for this。基本上你会有

 public interface iFruit
   {
      void Accept(FruitVisitor visitor);
   } 

所有可能的绘图访问只有一个类

public class DrawVisitor : FruitVisitor 
   {
      public override void Visit(Apple apple)
      {
         //draw the apple
      }

      public override void Visit(Banana banana)
      { 
         // draw the banana
      }
   }

在这里,你只有一个DrawVisitor而不是AppleDrawerBananaDrawer等,所有的抽奖代码都整齐地放在一个地方。您最终可能需要PlotterVisitorPrinterVisiter

答案 1 :(得分:4)

也许您可以使用Fruit方法创建抽象类Draw,并相应地抽象类FruitDrawer

例如:


public abstract class Fruit {
  ...
}

public abstract class FruitDrawer {
  public void Draw(Fruit f, Graphics g)
  {
    ...
  }
}

答案 2 :(得分:2)

你基本上反驳了Open-Closed Principle违规行为的规范示例,但使用的是水果而不是形状。

有人可以深入研究你的所有问题,但是如果你研究SOLID Principles,你会学到很多东西。

根据请求,我会深入研究您的代码。

首先我会将Draw推入IFruit界面,这样每个水果都负责绘制自己,而不是控制器类操纵一切。您可能以单一责任(SID在SOLID中)违规行为结束,但这将是另一次重构。这里重要的是水果自我绘制,控制器 开放用于扩展(添加更多水果类)但 关闭修改(因为他们自己绘制,控制器永远不会改变)。

然后你最终得到一个简单的循环画出你的水果。

foreach(var fruit in fruits)
    fruit.Draw(...);

解决“如何不[打破]他们”评论...

了解它们是什么是学习和应用它们的第一步。避免破坏它们的最简单方法(并不简单)是对测试驱动开发非常严格。您作为示例提供的大部分内容都很难通过TDD有意识地完成。换句话说,如果您了解执行TDD的SOLID原则,那么您最终不会得到您发布的代码。

答案 3 :(得分:1)

我认为最好这样做:

interface IFruit
{
   void Draw();
}

class Banana : IFruit
{
   void Draw()
   {
      BananaDrawer drawer = new BananaDrawer();
      drawer.Draw();
   }
}

并简单地使用它:

foreach(var fruit in fruits)
   fruit.Draw();

事实上,通过这种方式,您可以为相关水果中的特定抽屉运行额外的初始化。