我有不同的水果类,都实现了相同的界面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方法?
我正在考虑一种工厂模式,但我真的不知道该怎么做。
我的水果类是否必须将抽屉作为财产?或者我可以调用抽屉工厂方法吗?
这是我在当前项目中发现很多的模式,我找不到满足我的解决方案。
答案 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);
}
}
有时您需要Drawer
,Plotter
和Printer
,因此您的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
而不是AppleDrawer
,BananaDrawer
等,所有的抽奖代码都整齐地放在一个地方。您最终可能需要PlotterVisitor
,PrinterVisiter
等
答案 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();
事实上,通过这种方式,您可以为相关水果中的特定抽屉运行额外的初始化。