抽象工厂模式-具体产品之间的依赖关系
本文关键字:之间 依赖 关系 工厂 模式 -具 抽象 | 更新日期: 2023-09-27 18:25:48
请阅读下面的代码,问题在最后。
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-我认为您的SimpleGraphicsFactory
确实是抽象工厂的一个很好的例子。
2-SimpleSquare
转换为SimpleCanvas
是完全合适的,因为它们都是同一个"族"的一部分,由同一个混凝土工厂创建。回想抽象工厂的定义(重点是我的):
提供用于创建相关或从属族的界面对象,而不指定其具体类。
这种设计模式的含义是,它创建的类可以假设/要求它们与来自同一族的类一起使用。
使用.NET世界中的另一个示例,System.Data
命名空间的作用方式类似。System.Data.Sql
命名空间中的对象将无法与System.Data.Oracle中的对象一起使用。不能在需要OracleParameter
的地方传递SqlParameter
。选择族并留在族中。
3-我说不出你在想做什么,你;我需要详细评论,我会重新回复我的回答。我希望ComposedShape
具有一个方法Add(Shape s)
,该方法允许调用者向复合(容器)添加多个形状。但也许我误解了。
如果我理解您的意图,那么您正在尝试模仿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
)。