|
|
 | | From: | Jimmy Cerra | | Subject: | Which pattern does this conform to? | | Date: | Mon, 17 Jan 2005 20:01:20 GMT |
|
|
 | Hi. I've been struggling lately while designing a framework [1, 2], so I examined some existing solutions. I noticed a bunch of frameworks organized in the following fashion, so I'm wondering if it is a specific pattern.
The problem is that using an abstract factory to build families of related objects is awkward when those objects depend on their own family's types rather than their base types. This could violate the Liskov Substitution Principle [1]. For instance, consider the abstract toolkit:
] package AbstractWidgets; ] interface AbstractFactory { ] Window getWindow(); ] Button getButton(); ] ... ] } ] interface Window { ] void add(Button bb); ] ... ] } ] interface Button {...}
Two different implementations may not have compatible look-and-feels (i.e. Motif vs. WindowsXP based) or depend on incompatible drawing libraries (i.e. GTK vs. QT based). However, a client could still associate these two implementations:
] Window ww = aGtkBasedToolkit.getWindow(); ] Button bb = aQtBasedToolkit.getButton(); ] ww.add(bb);
That's not good. It seems to be especially hard to prevent these kinds of mistakes in languages without built-in templates or generics.
The problem seems to have been avoided in some frameworks by making the dependent objects responsible for creating their dependencies:
] package AbstractWidgets; ] interface AbstractFactory { ] Window getWindow(); ] ... ] } ] interface Window { ] Button createButton(); ] ... ] } ] interface Button {...}
Now the implementations are free to define their own quirks without violating the LSP:
] package GtkWidgets; ] import AbstractWidgets.*; ] class GtkBasedToolkit implements AbstractFactory { ] Window getWindow() { ] return new GtkWindow; ] } ] ... ] } ] class GtkWindow implements Window { ] private static GtkLib gg = new GtkLib(); ] Button createButton() { ] return new GtkButton(gg); ] } ] ... ] } ] class GtkButton implements Button { ] protected Button(GtkLib gg) {...} ] ... ] }
Clients then use them like this:
] Window ww = aGtkBasedToolkit.getWindow(); ] Button bb = ww.createButton();
Disadvantages (that I can see) of this design include:
* It is hard to change the framework to support new Objects.
* It may violate the single responsibility principle, since classes are responsible for their behavior and creating other objects.
One example of the pattern is the Document Object in the W3C's DOM. You create it's components with the instance rather than the Builder that creates the Document Object:
] Document dd = aDocumentBuilder.parse(file); ] Element ee = dd.createElement(tagName); ] // use ee
Another example is in JDBC. To create a statement to query a databse, one uses a Connection rather than a DriverManager object:
] Connection cc = DriverManager.getConnection(urlString); ] Statement ss = cc.createStatement(); ] ResultSet rr = ss.executeQuery(sqlString);
Is there a name for this pattern? Am I misinterpreting a classic pattern in these cases?
-- Jimmy Cerra https://nemo.dev.java.net
[1] http://groups-beta.google.com/group/comp.object/ browse_frm/thread/cf00bdcc47966776/e7685d427e30a5f1
[2] http://groups-beta.google.com/group/comp.object/ browse_frm/thread/aec14c5f186e804e/e5032a71e0cce334
|
|
 | | From: | Andrew McDonagh | | Subject: | Re: Which pattern does this conform to? | | Date: | Mon, 17 Jan 2005 23:09:10 +0000 |
|
|
 | Jimmy Cerra wrote: > Hi. I've been struggling lately while designing a framework [1, 2], so > I examined some existing solutions. I noticed a bunch of frameworks > organized in the following fashion, so I'm wondering if it is a specific > pattern. > > The problem is that using an abstract factory to build families of > related objects is awkward when those objects depend on their own > family's types rather than their base types. This could violate the > Liskov Substitution Principle [1]. For instance, consider the abstract > toolkit: > > ] package AbstractWidgets; > ] interface AbstractFactory { > ] Window getWindow(); > ] Button getButton(); > ] ... > ] } > ] interface Window { > ] void add(Button bb); > ] ... > ] } > ] interface Button {...} > > Two different implementations may not have compatible look-and-feels > (i.e. Motif vs. WindowsXP based) or depend on incompatible drawing > libraries (i.e. GTK vs. QT based). However, a client could still > associate these two implementations: > > ] Window ww = aGtkBasedToolkit.getWindow(); > ] Button bb = aQtBasedToolkit.getButton(); > ] ww.add(bb); > > That's not good. It seems to be especially hard to prevent these kinds > of mistakes in languages without built-in templates or generics.
I think this is the cause of your confusion. Typically you wouldn't have access to the actual concrete factory in your application. There would only be access to the AbstractFactory which is initialised with the appropriate Concrete factory. Therefore you would not be able to have this invalid association.
The client code gets the concrete factory (via an interface) from the Abstractfactory. From the Concrete factory it would call the generic createBlarBlar methods. But the client code does not know what actual concrete factory it is using.
public class WindowingAbstractFactory {
public static WindowFactory getFactory() { return m_concreteFactory; }
private WindowingAbstractFactory() { // Load appropriate Concrete factory depending upon platform // if (MSwindows) // m_concreteFactory = new MsWindowsConcreteFactory // else if (Mac) // m_concreteFactory = new MacConcreteFactory // ...
}
private static WindowFactory m_concreteFactory; }
public interface WindowFactory {
Frame createFrame(); Button createButton();
}
public class MsWindowsFactory implements WindowFactory {
public Frame createFrame() { //create an appropriate MS Windows frame }
public Button createButton() { ... }
}
public class GtkWindowFactory implements WindowFactory {
public Frame createFrame() { // create the appropriate GTK window }
public Button createButton() { ... }
}
|
|
 | | From: | Jimmy Cerra | | Subject: | Re: Which pattern does this conform to? | | Date: | Tue, 18 Jan 2005 04:48:48 GMT |
|
|
 | Andrew McDonagh wrote: > I think this is the cause of your confusion. Typically you wouldn't > have access to the actual concrete factory in your application. There > would only be access to the AbstractFactory which is initialised with > the appropriate Concrete factory. Therefore you would not be able to > have this invalid association. > > The client code gets the concrete factory (via an interface) from the > Abstractfactory. From the Concrete factory it would call the generic > createBlarBlar methods. But the client code does not know what actual > concrete factory it is using.
Many Abstract Factories use that way to choose a particular implementation. I believe that both java.sql.DriverManager and javax.xml.parsers.DocumentBuilderFactory both use that idiom, for example. And it is a good design.
However, it isn't appropriate for all cases. The example in GoF uses a different idiom, and a design that I'm considering uses factory methods to select the appropriate AF. I also dislike static factories from a stylistic POV; often they are more trouble than they're worth.
P.S. I don't think anyone in my earlier thread [1] raised that particular objection to the design. The comments generally were one of:
1. It violated the LSP, so change the object's responsibilities. 2. It violated the LSP, but that's OK. 3. It didn't violate it and wasn't a design flaw. 4. It didn't violate it and wasn't a design flaw, but a usage flaw (?).
I took (1) to heart while considering (2)-(4), and recognized this pattern. It is still entirely possible that I'm mistaken. Could you elaborate on these points?
-- Jimmy Cerra
[1] http://groups-beta.google.com/group/comp.object/ browse_frm/thread/cf00bdcc47966776/e7685d427e30a5f1
|
|
 | | From: | iamfractal at hotmail.com | | Subject: | Re: Which pattern does this conform to? | | Date: | 18 Jan 2005 01:52:23 -0800 |
|
|
 | Andrew McDonagh wrote: > Jimmy Cerra wrote: > > Hi. I've been struggling lately while designing a framework [1, 2], so > > I examined some existing solutions. I noticed a bunch of frameworks > > organized in the following fashion, so I'm wondering if it is a specific > > pattern. > > > >
[Herr Snipski]
Hi, folks!
I agree with Andrew that you usually don't have direct access to the concrete factory class; but I usually do allow the user to switch implementations behind the factory's interface. I usually use a parameterized factory method for this, so the user can do the following (if he wishes):
Factory gtkFactory = options.getFactory(Options.GTK_FACTORY); Factory qtFactory = options.getFactory(Options.QT_FACTORY);
So at least in my applications, the user can play havok by passing GTK-implementations to QT-implementations.
> > That's not good. It seems to be especially hard to prevent these kinds > > of mistakes in languages without built-in templates or generics. > >
Engineering is compromise. It's of course, "Not good," if the user mixes implemenations and causes a program to crash. But the abstract factory pattern offers the advantage that, if the user is careful, he can have identical application code working with different toolkit implementations.
This, of course, you're fully aware of; I just wanted to point out that it would be rash to dump the abstract factory pattern on the grounds of any one disadvantage. Not that the OP proposed dumping it, of course.
Getting back to that, "It seems to be especially hard to prevent these kinds of mistakes ..." It's especially hard to prevent the user making all sorts of mistakes. Some of them do it for a living, poor things.
I would say that the answer is good ol' documentation. Tell your users not to mix impelmentations. They'll still do it, but not for very long; and you'll be able to say, "Told ya so." The benefit of uniform access to the different implementations outweighs the cost potential crashes. If you want type-safe prevention of such crashes, then drop the abstract factory pattern and, "Program to an implementation, not an interface." (Ouch.)
Furthermore, the OP asks, "Is there a name for this pattern? Am I misinterpreting a classic pattern in these cases?"
I don't see a strong pattern in the Window creating the Button, rather than the factory creating Window and Button itself. Perhaps mild delegation.
I don't think you're misinterpreting a classic pattern; perhaps you thought that all design patterns were perfect or flawless: they are not; they are engineering solutions whose benefits and costs must be weighed per-application, and whose limitations should not be overlooked.
Not long now to Episode III. I'd bet Darth wouldn't tolerate users mixing implementations. "Apologies accepted, user Nieder." ..ed
www.EdmundKirwan.com - Home of The Fractal Class Composition.
|
|
 | | From: | Daniel T. | | Subject: | Re: Which pattern does this conform to? | | Date: | Wed, 19 Jan 2005 12:32:42 GMT |
|
|
 | Jimmy Cerra wrote:
> Hi. I've been struggling lately while designing a framework [1, 2], so > I examined some existing solutions. I noticed a bunch of frameworks > organized in the following fashion, so I'm wondering if it is a specific > pattern. > > The problem is that using an abstract factory to build families of > related objects is awkward when those objects depend on their own > family's types rather than their base types. This could violate the > Liskov Substitution Principle [1]. For instance, consider the abstract > toolkit: > > ] package AbstractWidgets; > ] interface AbstractFactory { > ] Window getWindow(); > ] Button getButton(); > ] ... > ] } > ] interface Window { > ] void add(Button bb); > ] ... > ] } > ] interface Button {...} > > Two different implementations may not have compatible look-and-feels > (i.e. Motif vs. WindowsXP based) or depend on incompatible drawing > libraries (i.e. GTK vs. QT based). However, a client could still > associate these two implementations: > > ] Window ww = aGtkBasedToolkit.getWindow(); > ] Button bb = aQtBasedToolkit.getButton(); > ] ww.add(bb); > > That's not good. It seems to be especially hard to prevent these kinds > of mistakes in languages without built-in templates or generics. > > The problem seems to have been avoided in some frameworks by making the > dependent objects responsible for creating their dependencies: > > ] package AbstractWidgets; > ] interface AbstractFactory { > ] Window getWindow(); > ] ... > ] } > ] interface Window { > ] Button createButton(); > ] ... > ] } > ] interface Button {...} > > Now the implementations are free to define their own quirks without > violating the LSP: > > ] package GtkWidgets; > ] import AbstractWidgets.*; > ] class GtkBasedToolkit implements AbstractFactory { > ] Window getWindow() { > ] return new GtkWindow; > ] } > ] ... > ] } > ] class GtkWindow implements Window { > ] private static GtkLib gg = new GtkLib(); > ] Button createButton() { > ] return new GtkButton(gg); > ] } > ] ... > ] } > ] class GtkButton implements Button { > ] protected Button(GtkLib gg) {...} > ] ... > ] } > > Clients then use them like this: > > ] Window ww = aGtkBasedToolkit.getWindow(); > ] Button bb = ww.createButton(); > > Disadvantages (that I can see) of this design include: > > * It is hard to change the framework to support new Objects. > > * It may violate the single responsibility principle, since classes are > responsible for their behavior and creating other objects. > > One example of the pattern is the Document Object in the W3C's DOM. You > create it's components with the instance rather than the Builder that > creates the Document Object: > > ] Document dd = aDocumentBuilder.parse(file); > ] Element ee = dd.createElement(tagName); > ] // use ee > > Another example is in JDBC. To create a statement to query a databse, > one uses a Connection rather than a DriverManager object: > > ] Connection cc = DriverManager.getConnection(urlString); > ] Statement ss = cc.createStatement(); > ] ResultSet rr = ss.executeQuery(sqlString); > > Is there a name for this pattern? Am I misinterpreting a classic > pattern in these cases?
Your problem, IMO, is that you are allowing multiple factories to exist. Make AbstractWidgets a singleton and the clients won't be able to create two of them at the same time.
|
|
|