【发布时间】:2013-08-01 20:46:15
【问题描述】:
请阅读下面的代码,问题在最后。
using System;
using System.Collections.Generic;
namespace Graphics
{
public interface IGraphicsFactory
{
ICanvas CreateCanvas();
Square CreateSquare();
ComposedShape CreateComposedShape();
}
public class SimpleGraphicsFactory : IGraphicsFactory
{
public Square CreateSquare()
{
return new SimpleImpl.SimpleSquare();
}
public ComposedShape CreateComposedShape()
{
return new SimpleImpl.SimpleComposedShape();
}
public ICanvas CreateCanvas()
{
return new SimpleImpl.SimpleCanvas();
}
}
public interface ICanvas
{
void AddShape(ShapeBase shape);
void Render();
}
public abstract class ShapeBase
{
public abstract void Paint(ICanvas canvas);
}
public abstract class Square : ShapeBase
{
public int size;
}
public abstract class ComposedShape : ShapeBase
{
public int size;
public ShapeBase InternalShape1 { get; set; }
public ShapeBase InternalShape2 { get; set; }
}
}
namespace Graphics.SimpleImpl
{
internal class SimpleSquare : Graphics.Square
{
public void Init()
{
// do something really important
}
public override void Paint(ICanvas canvas)
{
Init();
//?? how to avoid the type cast? (and I want to keep the DrawLine out of the ICanvas interface)
SimpleCanvas scanvas = (canvas as SimpleCanvas);
scanvas.DrawLine();
scanvas.DrawLine();
scanvas.DrawLine();
scanvas.DrawLine();
}
}
internal class SimpleComposedShape : Graphics.ComposedShape
{
public void Init()
{
//?? how can I call `InternalShape1.Init', preferably without type casts? (and I want to keep `Init` out of the `ShapeBase` class)
// this.InternalShape1.Init();
// this.InternalShape2.Init();
}
public override void Paint(ICanvas canvas)
{
Init();
// TODO: draw the thing
}
}
internal class SimpleCanvas : Graphics.ICanvas
{
List<ShapeBase> shapes = new List<ShapeBase>();
public void AddShape(ShapeBase shape)
{
shapes.Add(shape);
}
public void Render()
{
foreach (ShapeBase s in shapes)
{
s.Paint(this);
}
}
public void DrawLine()
{
}
}
}
namespace Test
{
using Graphics;
class TestSimpleGraphics
{
static void Test1()
{
IGraphicsFactory fact = new SimpleGraphicsFactory();
ICanvas canvas = fact.CreateCanvas();
Square sq1 = fact.CreateSquare();
Square sq2 = fact.CreateSquare();
ComposedShape cs = fact.CreateComposedShape();
cs.InternalShape1 = sq1;
cs.InternalShape2 = sq2;
canvas.AddShape(cs);
canvas.Paint();
}
}
}
- 我的抽象工厂模式实现是否正确?
- 内部
SimpleSquare.Paint:可以避免类型转换吗? (并且我想将DrawLine排除在ICanvas接口之外) - 在
SimpleComposedShape.Init内部:我怎样才能调用InternalShape.Init,最好没有类型转换? (并且我想将Init排除在ShapeBase类之外)
【问题讨论】:
-
这是太多的代码来解决#1,但对于#2:如果不将其放入该接口(或不同的接口)或可能在某处使用泛型,就无法避免强制转换。此外,您需要在演员表之后检查 null 。对于#3,我只需将它添加到基类或使用通用 ComposedShape
where T : ShapeBase. -
如果你有
ICanvas实现的工厂,我将其命名为CanvasFactory。另外,您的ICanvas应该代表或抽象什么?我不明白它的目的。什么时候应该调用AddShape?为什么画布包含形状列表?ShapeBase.Paint内部应该发生什么?ICanvas实现是否负责绘制图形基元?在那种情况下,为什么不应该为此提供公共方法呢? -
@Groo:我的图形模型类似于“保留模式”图形库,请参阅en.wikipedia.org/wiki/Retained_mode。所以,把画布想象成类似于浏览器的 DOM 的东西。它不提供绘制线条或矩形的原语,只是将对象添加到其中,并且对象在内部呈现。
ShapeBase.Paint是抽象的,所以它不能有实体。而这整个事情只是我正在研究的其他东西的等效模型,它具有完全不同的语义,可能这就是示例代码不是最好的原因。 -
是的,我知道您想要存储 DOM,但关键是具体的
ICanvas应该存储 知道其实际实现的原始形状(对于例如,SimpleSquare应该有Paint(SimpleCanvas)方法,而不是Paint(ICanvas)方法)。 -
为了 KISS 的利益,您真的有多个实现系列吗?你真的会有一组 SimpleXXX 类,还有一组 ComplexXxx 类和一个 MediocreXxx 类吗?如果你不需要它,不要过于复杂。
标签: c# oop design-patterns language-agnostic factory-pattern