请阅读下面的代码,问题在最后。
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
类)答案 0 :(得分:1)
1 - 我认为你的SimpleGraphicsFactory
确实是抽象工厂的一个很好的例子。
2 - SimpleSquare
投射到SimpleCanvas
是完全合适的,因为它们都是同一个家庭&#34;的一部分,由同一个具体工厂创建。回想一下Abstract Factory的定义(重点是我的):
提供用于创建相关或依赖系列的界面 没有指定具体类的对象。
这种设计模式的含义是它创建的类可以假定/要求它们与同一族的类一起使用。
要使用.NET世界中的另一个示例,System.Data
命名空间的行为方式类似。来自System.Data.Sql
命名空间的对象不适用于System.Data.Oracle中的对象。您无法通过SqlParameter
预计OracleParameter
的位置。您选择家庭并留在家庭中。
3 - 我不知道你要做什么,你;我需要对细节发表评论,并且我会回复我的答案。我希望ComposedShape
有一个方法Add(Shape s)
,让调用者可以为复合(容器)添加多个形状。但也许我误解了。
答案 1 :(得分:0)
如果我的意图是正确的,那么您正试图模仿System.Drawing.Graphics
类(或HTML <canvas>
)的功能。
如果这是正确的,我会提出以下建议:
将IGraphicsFactory
重命名为ICanvasFactory
,并让它只创建具体的ICanvas
实现。删除CreateSquare
和其他方法,因为您不必通过工厂创建形状(唯一重要的是您的形状通过具体的ICanvas
实现)。
ICanvas
界面表示可以绘制原始形状(线条,圆圈,填充区域等)的画布。这意味着您应该公开允许调用者创建这些基元的公共方法。 Graphics
还提供了各种转换功能,但现在这可能是一种过度杀伤力:
interface ICanvas
{
void Clear();
void DrawLine(Point from, Point to, Pen pen);
void DrawCircle(Point center, Double radius, Pen pen);
void Paint();
/* and stuff like that */
}
Canvas
不应包含Shapes
的列表,而应包含基元列表。例如,当您调用ICanvas.DrawLine(...)
方法时,它应该创建一个Line
原语的实例并将其存储在内部列表中。
这些原始类的功能取决于ICanvas
的实际实现(它是绘制到位图还是打印机等)。您还将拥有一些隐藏的,与实现相关的数据(我将使用byte[]
,假装它存储某种类型的8位位图):
class BitmapCanvas : ICanvas
{
private readonly byte[] _bitmapData;
private readonly List<IBitmapShape> _primitives;
public BitmapCanvas(int width, int height)
{
_bitmapData = new byte[width * height];
_primitives = new List<IPrimitiveShape>();
}
public void DrawLine(...)
{
// different implementations will handle this part differently.
_primitives.Add(new BitmapLine(_bitmapData, from, to, pen));
}
public void Paint()
{
Clear(_bitmapData);
foreach (var shape in _primitives)
shape.Draw();
}
}
然后,具体的基本类将处理这个内部逻辑:
class BitmapLine : IBitmapShape
{
public void Draw()
{
// write to the underlying byte array
}
}
由于ICanvas
实现不会绘制实际形状,因此您不需要ShapeBase
类。但是,您需要一个模拟类来绘制图形基元(上面称为IBitmapShape
)。