Please read the code below, the questions are at the end.
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
: it is possible to avoid the type cast? (and I want to keep the DrawLine
out of the ICanvas
interface)SimpleComposedShape.Init
: how can I call InternalShape.Init
, preferably without type casts? (and I want to keep Init
out of the ShapeBase
class)1 - I think your SimpleGraphicsFactory
is indeed a good example of an Abstract Factory.
2 - It is completely appropriate that SimpleSquare
casts to SimpleCanvas
because they are both part of the same "family", created by the same concrete factory. Recall the definition of Abstract Factory (emphasis is mine):
Provide an interface for creating families of related or dependent objects without specifying their concrete classes.
The implication of this design pattern is that the classes created by it can assume / require they are being used with classes from the same family.
To use another example from the .NET world, the System.Data
namespaces acts in a similar way. Objects from the System.Data.Sql
namespaces will not work with objects from System.Data.Oracle. You cannot pass a SqlParameter
where an OracleParameter
is expected. You select the family and stay within the family.
3 - I cannot tell what you are trying to do, you;ll need to comment with details and I'll revies my answer to address. I would expect a ComposedShape
to have a method Add(Shape s)
that lets the caller add multiple shapes to the composite (container). But perhaps I misunderstand.