The abstract factory pattern is a common fixture of well designed code. As useful as it is, it can be an annoying pattern because the scaffolding to implementation ratio is quite high. It also breaks up code that most of the time should be together. Other patterns that reach this level of usage often benefit from being baked into the language framework, so why not this pattern.
Let's look at a fairly basic implementation:
public interface ButtonFactory
{
Button CreateButton();
Button CreateButton(Control parent);
}
public interface Button
{
void Draw();
}
public class WinButtonFactory : ButtonFactory
{
public Button CreateButton() { return new WinButton(); }
public Button CreateButton(Control parent) { return new WinButton(parent); }
}
public class WinButton : Button
{
internal WinButton() { ... implementation ... }
internal WinButton() { ... implementation ... }
public void Draw() { ... implementation ... }
}
public class SampleUser
{
public void UseFactory()
{
ButtonFactory factory = new WinButtonFactory();
Button parent = factory.Create();
Button child = factory.Create(parent);
}
}
This is okay, but if we had a little syntax help:
public interface Button
{
Button();
Button(Control parent);
void Draw();
}
public class WinButton : Button
{
public WinButton() { ... implementation ... }
public WinButton() { ... implementation ... }
public void Draw() { ... implementation ... }
}
public class SampleUser
{
public void UseFactory()
{
Factory<Button> factory = WinButton.GetFactory<Button>();
Button parent = factory;
Button child = factory(parent);
}
}
That's a fair bit more succinct, and it also allows the definition of the "Create" to reside within the interface, making it much more obvious to consumers of the API the relationship between the factory and the interface. The same is true for the implementation, where it's no longer necessary to either provide a workaround to the visibility issues that arise from having object initialization in a separate class.
Some might object on the basis that the use of the factory pattern extends beyond the example above, but those uses can still be accommodated:
public class ColoredButtonFactory : Factory<Button>
{
private Color _color;
private Factory<Button> _factory;
public ColoredButtonFactory(Color color, ButtonFactory factory);
{
_color = color;
_factory = factory;
}
public Button()
{
Button button = _factory();
button.Color = _color;
return button;
}
}
Which is equivalent to this implementation without language level factory support:
public class ColoredButtonFactory : ButtonFactory
{
private Color _color;
private ButtonFactory _factory;
public ColoredButtonFactory(Color color, ButtonFactory factory);
{
_color = color;
_factory = factory;
}
public Button CreateButton()
{
Button button = factory.CreateButton();
button.Color = _color;
return button;
}
}
An alternative (with pluses and minuses) the language level factory support would allow for is:
public class ColoredButtonFactory<TButton> : Factory<TButton>
where TButton : Button
{
private Color _color;
public ColoredButtonFactory(Color color);
{
_color = color;
}
public TButton()
{
TButton button = new TButton();
button.Color = _color;
return button;
}
}
3 comments:
It seems easier to have some kind of language support where you inject the dependency by passing the class as a value, and then instantiate it directly, and the constructors could be described by the interface.
For example:
public interface Button
{
Button();
Button(Control parent);
void Draw();
}
public class SampleUser
{
public SampleUser(Class<? extends Button> buttonClass)
{
this.buttonClass = buttonClass;
}
public void UseFactory()
{
Button parent = this.buttonClass();
Button child = this.buttonClass(parent);
}
}
Right now this kind of technique requires cumbersome and slow reflection techniques, so people use factories to get that layer of indirection.
You rarely need factories in C#. The new() generic constraint can cover you, or you can use reflection if you really want to call a specific constructor:
public interface IButton
{
void Draw();
void Initialize(string name);
}
public class FooButton : IButton { /* stuff */ }
public class BarButton : IButton { /* stuff */ }
public class Test
{
public void SomethingThatNeedsAFactory<T>(string name) where T : IButton, new()
{
// two-step initialization
T button = new T();
button.Initialize(name);
// now you can use it
button.Draw();
}
public void Test()
{
SomethingThatNeedsAFactory<FooButton>("foo");
SomethingThatNeedsAFactory<BarButton>("bar");
}
}
Bob, reflection is powerful, but it's also slow and clunky. For something as common as this I would want to give developers a way to avoid that cost.
The new() constraint also has limitations beyond limiting to parameterless constructors. Personally I think it's sign of a flaw elsewhere (possibly a lack of this very proposal) that causes it to exist at all. Why is a constructor (and parameterless only too) special cased instead of handled through interfaces like other methods? If the language offered support like what I describe, the new() constraint would be unnecessary.
I prefer not to need an informal pattern like Initialize too because it has implicit semantics that are easily violated (calling Initialize twice or zero times).
Post a Comment