views:

443

answers:

2

Im somewhat new to design patterns and this is my first post in stackoverflow, so hopefully this question will make sense. Ive created an abstract factory to handle generating xml strings for different chart vendors (dundas, flash, etc...). Below is a code outline of my factory (I can include more if it helps.) Id like for my client to be able to set properties that will be common among all types of charts (caption, animation, etc.) So a client could do something like this:

        GraphCreator fusion = new FusionGraphs();

        //set the props for the graph
        fusion.Caption = "Fusion 2D Line Chart";

What is the best way to do this? Right now, Im setting properties in the abstract creator so the client can have access to them, but Im also having to duplicate these properties in my factory so I can have access to them in building the xml.

//this is the abstract factory

public interface IXMLFactory
{

    //add interface methods
    IRoot makeRoot();
    IRootAttrib makeRootAttrib();
    INodes makeNodes();
    INodeAttrib makeNodeAttrib();

}

//this is the abstract creator

public abstract class GraphCreator
{

    public virtual Graph getGraph(Graph.Types graphType)
    {
        //abstract product
        Graph graph;

        //abstract product creation
        graph = buildGraph(graphType); 

        graph.draw(); 

        return graph;

    }

    public abstract Graph buildGraph(Graph.Types graphType);

}

//this is the concrete creator

public class FusionGraphs : GraphCreator
{
    Graph g = null;

    //XML parts for fusion 2D multi series
    IXMLFactory xmlFactory;


    //use xml parts that are needed for the type of fusion graph requested
    public override Graph buildGraph(Graph.Types graphType)
    {
        switch (graphType)
        {
            case Graph.Types.Single2DLine:
                xmlFactory = new Fusion2DSingleXMLFactory();
                g = new Single2DLineGraph(xmlFactory);
                xmlFactory.Caption = base.Caption;                     
                break;
            case Graph.Types.Single2DBar:
                xmlFactory = new Fusion2DSingleXMLFactory();
                g = new Single2DBarGraph(xmlFactory);

                break;
        }


        return g;
    }


}
A: 

I think somehow your problem is how to share configuration information between the class which calls the factory and the factory itself. Implement a separate class that holds the configuration information (e.g. caption), and then give a reference to it both to the factory and the creator.

antti.huima
+1  A: 

I'm not sure if I'm understanding the whole scope of this, but this seems like you should be able to create some object that represents the shared properties of the different types of graphs, and expose that object as a property of the abstract creator, accessible by the concrete creators, and maybe even passed as a parameter to the individual xmlFactory constructors. The caller can set these properties directly on that object by accessing the property that exposes it, and the concrete classes can read them from that object. That does mean, however, that the caller goes through one more level of indirection to access these common properties.

I don't quite understand what duplication you have. You have properties implemented on the abstract creator, but you said you also "duplicate these properties in the factory"? Are you referring to the concrete creator? I don't see why -- you are referring to base.Caption, so why would you need to duplicate anything in FusionGraphs, if it's all inherited from GraphCreator, and you're using the base class implementation of it?

BlueMonkMN
I think I left out the duplication part. I actually create identical properties in the GraphCreator class for caption. It seems that I need to make the props accessible to the client (via the graph class or something) then make the props in the xmlfactory so they can be accessed internally.
rahkim
Bottom line is that I cant see a way around creating the same property twice. Once for the client, and once for the factory. Is there a way to create the property once so the client can see it, and the concrete subclasses can access it?
rahkim
Could you do without the properties on your xmlFactory? Could you instead have an interface "ICommonGraphProps" implemented by GraphCreator, and allow xmlFactory to access members of ICommonGraphProps whenever it needs to read the value of a common property?
BlueMonkMN
I created a common interface that GraphCreator implements. But Im struggling with how to allow the xmlFactory members access it for values.
rahkim
Instead of exposing many properties such as "Caption" on the xmlFactory, expose a single property of type ICommonProperties (or whatever you called the interface) with which the caller can pass the interface to the xmlFactory, and it can store that reference rather than individual values.
BlueMonkMN
That helps. I created the ICommon interface with some properties like "Caption." Then my abstract xmlFactory implemented a property of type ICommon. My concrete factories (like Fusion2DSingleXMLFactory) implement xmlFactory. Then my concrete creators can do this:
rahkim
xmlFactory.common.Caption = base.Caption;So I expose the common properties in my abstract Creator like Caption so the user can say myGraph.Caption = "test";
rahkim
Follow up to make sure Im doing this right. In my xmlFactory I have some methods like IRoot makeRoot(). I need to get the property (Caption) into my implementation of this method. Im guessing the best way would be to put it in the contstructor like:public FusionRoot(string Caption)
rahkim
That doesn't look quite like what I was envisioning. You shouldn't have to use code like xmlFactory.common.Caption = base.Caption. You should be able to code xmlFactory.common = base, and they whenever xmlFactory references caption internally it is linked to base's implementation of it.
BlueMonkMN
I think your makeRoot function should be able to refer to common.Caption (which accesses GraphCreator's ICommon interface implementation for caption... whatever you called ICommon.)
BlueMonkMN
Hmmm... strugglin' a bit here, sorry. I wish I had more space to post code. Im guessing that I still need the common props in the ICommon interface? Then in my creator class I dont use:xmlFactory.common.Caption = base.Caption;Instead I should use:xmlFactory.common = base;
rahkim
I think the answer is in here somewhere, but if you have some more time I could use a little more guidance on how to use this ICommon interface.
rahkim
Yes, you'd need to define all the properties in ICommon (should be pretty compact because it's just an interface) then implement them in them in GraphCreator (store the values). Then to transfer the common properties to xmlFactory, set xmlFactory.common = (your GraphCreator or ICommon implementor).
BlueMonkMN
I like this. Here is what I did.xmlFactory.common = this; But in my concrete implementation of Graph (where I 'draw' the graph), I had to do this to access the caption:string caption = xmlFactory.common.CaptionIs that acceptable practice? If so, Im set! Thanks for the help.
rahkim
I'm a little confused. I thought xmlFactory was to generate XML, not to be used when drawing the graph. In any case, I'm no formal design pattern expert (I just rely on my instincts), but if you didn't have to change the visibility of xmlFactory.common to be able to use that line, I'd say it's good.
BlueMonkMN
Without seeing all your code it's hard to get a full mental picture, but if you'd rather refer to xmlFactory.caption instead of xmlFactory.common.caption, you could make xmlFactory have an inheritance structure too and implement ICommon in xmlFactory's base class.
BlueMonkMN
I appreciate the advice. I learned something new that I think we will be very helpful. Thanks!
rahkim