.Net Code Monkey RSS 2.0
 Thursday, April 10, 2008

Back to Part 1

Review Of Progress So Far

So far we have two concrete shape classes, Circle and Rectangle that derive from a base shape class. The base shape class implements the IShape interface and the concrete classes implement in turn the IShape interface as well as their respective shape interfaces. The next step is to create a factory class to create our shapes.

Working In The Factory

So, we need to add to the solution, another new class library project which we will call Factories. This will house our ShapeFactory class. The ShapeFactory class will be responsible for creating our shape objects. It will be able to create and return an object that implements the IShape interface. So far we have ensured that both our circle and rectangle inherently implement the IShape interface for two reasons. One, because they both derive from the BaseShape object and we know that implements IShape, and two, because they implement ICircle and IRectangle which in turn implements IShape, as well. (Maybe some one with a little more experience than I, could advise if this is an acceptable practise. -> I can't see why not!)

But before we create our ShapeFactory we have one more thing to do. Because we may want to use more than one factory in the future to create our shapes we are first going to create another interface, which will describe what a ShapeFactory need to create some shape objects. This will be the IShapeFactory interface, and we are going to add that for the sake of simplicity to our ShapeInterfaces project

using System;

namespace Playground.ShapeMaker.ShapeInterfaces
{
    public interface IShapeFactory
    {
        /// <summary>
        /// Use this method to create a shape.
        /// </summary>
        /// <returns>An object that implements the IShape interface.</returns>
        IShape CreateShape();
    }
}

As you can see we have determined that any factory that implements IShapeFactory will have to contain a method that returns an object implementing IShape. The problem is, how do we determine what sort of shape we want to get? Well even though we only have two shapes at the moment for future expansion we will need an enumeration to represent these. As enumeration is to be used in every class that implements IShapeFactory and will need to be a parameter in the CreateShape method, it seems logical to me to place the enumeration in the ShapeInterfaces project. If any one can advise whether this is good practise or not, please do! So, let us add the Shape enumeration.

using System;

namespace Playground.ShapeMaker.ShapeInterfaces
{
    /// <summary>
    /// Represents the types of shapes that can be made
    /// </summary>
    public enum Shape
    {
        /// <summary>
        /// Indicates the shape is to be a circle
        /// </summary>
        Circle,

        /// <summary>
        /// Indicates the shape is to be a rectangle
        /// </summary>
        Rectangle
    }
}
We can now modify the CreateShape method of the IShapeFactory interface to accept a Shape enumerated value.
public interface IShapeFactory
 {
     /// <summary>
     /// Use this method to create a shape.
     /// </summary>
     /// <param name="shape">Indicates the type of shape to be made.</param>
     /// <returns>An object that implements the IShape interface.</returns>
     IShape CreateShape(Shape shape);
 }

So it's back to the Factories project now to create the ShapeFactory class that will provide us with some shapes. To create some shapes we need to add a reference to both the BusinessEntity and the ShapeInterfaces projects into the Factories project. After adding our new ShapeFactory class into the Factories project we need to import the BusinessEntity and the ShapeInterfaces namespaces into the top of it. We now need to make the ShapeFactory class implement IShapeFactory.

using System;
using Playground.ShapeMaker.BusinessEntity;
using Playground.ShapeMaker.ShapeInterfaces;

namespace Playground.ShapeMaker.Factories
{
    public class ShapeFactory : IShapeFactory
    {

        #region IShapeFactory Members

        public IShape CreateShape(Shape shape)
        {
            throw new Exception("The method or operation is not implemented.");
        }

        #endregion
    }
}

We are now going to create a pair of private methods that will create our two shapes. Although these methods will create two concrete objects the methods will return not the objects but their interfaces. First though we will just create the method stubs. Then we will modify the CreateShape() method to call one or the other of these two methods depending upon the enumerated value passed in.

#region IShapeFactory Members

 /// <summary>
 /// Creates a shape and returns it.
 /// </summary>
 /// <param name="shape">Indicates the shape to create.</param>
 /// <returns>A shape object that implements IShape.</returns>
 public IShape CreateShape(Shape shape)
 {
     IShape createdShape = null;
     switch (shape)
     {
         case Shape.Circle:
             createdShape = CreateCircle();
             break;

         case Shape.Rectangle:
             createdShape = CreateRectangle();
             break;

         default:
             throw new Exception("Unknown shape encountered in CreateShape() method.");
     }
     return createdShape;
 }

 #endregion

 /// <summary>
 /// This method creates a circle object.
 /// </summary>
 /// <returns>A shape object that implements ICircle.</returns>
 private ICircle CreateCircle()
 {

 }

 /// <summary>
 /// This method creates a rectangle object.
 /// </summary>
 /// <returns>A shape object that implements IRectangle.</returns>
 private IRectangle CreateRectangle()
 {

 }

We have used switch / case to select which method will create our shape object. Notice that we use the default case to throw an exception if we encounter a shape that we haven't catered for. This is in case at any time the shape enumeration is extended to provide more shapes. Notice also that although the CreateShape() method returns an object that implements IShape, the two worker methods return ICircle and IRectangle. This works because both ICircle and IRectangle both implement the IShape interface, and that is what CreateShape() will return.

Anyway let us crack on and 'flesh out' the CreateCircle() worker method! Because our shapes using the System.Drawing namespace, our factory that is going to create these will need to also. So we need to add a reference to and import that namespace for the ShapeFactory. (Incidentally have you noticed the more you look at the word "circle" the more wrong it looks!)

 /// <summary>
 /// This method creates a circle object.
 /// </summary>
 /// <returns>A shape object that implements ICircle.</returns>
 private ICircle CreateCircle()
 {
     Circle circle = new Circle();
     circle.Center = new Point(250,250);
     circle.Diameter = 50;
     return (ICircle)circle;
 }

Now I would normally use constructor in my code, but this example isn't to show best practise of constructing objects, so I am going to construct my objects using the properties today. You will probably also notice that I am casting the newly constructed Circle object to the type of ICircle before returning it. You don't need to do this but I believe it is good practise. (Can any one confirm this please?)

At this point I have just realised an error with choosing a rectangle for a shape, due to a conflict with the System.Drawing.Rectangle object. Well we could change the shape to a square or a triangle.... but we have got this far, so I am going to stick with it. It will however mean there will be some ugly code with the BusinessEntity namespace forced in here and there!!!

 /// <summary>
 /// This method creates a rectangle object.
 /// </summary>
 /// <returns>A shape object that implements IRectangle.</returns>
 private IRectangle CreateRectangle()
 {
     BusinessEntity.Rectangle rectangle = 
         new BusinessEntity.Rectangle();
     rectangle.Center = new Point(300, 300);
     rectangle.Size = new Size(400, 200);
     return (IRectangle)rectangle;
 }

Now that should be it for our ShapeFactory for the moment. If all has gone well for you then your project should build without errors.

To sum up we have created an interface for this and any other shape factory to implement, an enumeration that represents all the available shapes we may wish to build, and a factory class that can build any of our two current shapes. the next step is to create a graphical user interface that will call the factory and receive a couple of shape objects for it's trouble.

Part 3

Thursday, April 10, 2008 6:28:13 PM (GMT Standard Time, UTC+00:00)  #    Comments [2] -
.Net | C# | Classes | Factory Pattern | Inheritance | Interfaces
Thursday, April 10, 2008 8:43:49 PM (GMT Standard Time, UTC+00:00)
Hmmm, interesting!! I'm not an expert on Factory patterns but here's a few of my observations:-

In this particular example I would suggest the use of the BaseShape class is a waste of time. As it doesn't implement any base behaviour it little more than a duplicate of your IShape interface. In addition I don't think anywhere in your code you return objects of type BaseShape, you always use the interfaces.

One other point you might want to consider is how you request a shape out of your factory. Traditionally I would favour using an enumeration to define the available shapes, however, I have seen some examples where a String can be passed in to define the shape, e.g. CreateShape("circle"). My understanding of using the untyped string parameter convention is it makes it much easier to plug in a changed or new version of the Factory class without the referencing project needing to know or be recompiled. The disadvantage is you lose type safety and have to know what shapes are available and their names before you can create one. I guess on the latter you could expose all the available shape names in a collection of strings from within the factory. Thus you could ask the factory what shapes are available prior to creating one.

Just some thoughts
joboy
Thursday, April 10, 2008 9:00:28 PM (GMT Standard Time, UTC+00:00)
@Joboy:

Yes, you are correct my BaseShape class, only holds the data for the center of the and nothing more; certainly no behavior, as you mentioned. With such simple objects it probably is a waste of time. I will have to see as I extend the objects whether it becomes more worth while or whether to scrap that base class.

I can see where you are coming from with passing in a string to indicate the type of shape that you want out. As you said, it would definately provide a lot more freedom for swapping factories in and out, and I like the fact that a colection of strings could be available from the factory so the calling client could check before making the call to the factory.

My only reservation is I have only recently come over to a strongly typed language now I have got to grips with it I feel reluctant to let it go!!! Meh!

Definately good food for thought for future projects, though! And thank you for your input, it is very much appreciated!

DW.
All comments require the approval of the site owner before being displayed.
Name
E-mail
(will show your gravatar icon)
Home page

Comment (Some html is allowed: a@href@title, b, blockquote@cite, em, i, strike, strong, sub, sup, u) where the @ means "attribute." For example, you can use <a href="" title=""> or <blockquote cite="Scott">.  

Enter the code shown (prevents robots):

Live Comment Preview
Archive
<April 2008>
SunMonTueWedThuFriSat
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910
Blogroll
 Clemens Vasters
 Harry Pierson
Passion * Technology * Ruthless Competence
 Joshua Flanagan
A .NET Software Developer
 Michael Schwarz's Blog
Developing applications on the Microsoft platform since Windows 3.1!
 Omar Shahine
Yet another Microsoft blogger
 Scot GU
Scott Guthrie lives in Seattle and builds a few products for Microsoft
 Scott Hanselman
Programming Life and the Zen of Computers
 Tom Mertens
Tom's corner
About the author/Disclaimer

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2012
Duane Wingett
Sign In
Statistics
Total Posts: 39
This Year: 4
This Month: 0
This Week: 0
Comments: 39
Themes
Pick a theme:
All Content © 2012, Duane Wingett
DasBlog theme 'Business' created by Christoph De Baene (delarou)