CSC/ECE 517 Fall 2010/ch5 5e mf: Difference between revisions
No edit summary |
No edit summary |
||
Line 60: | Line 60: | ||
end | end | ||
</pre> | </pre> | ||
One major drawback of using the factory pattern is that each abstraction needs its own factory. In this case, the only abstraction is thing, but if there were many abstractions this could lead to a lot of unnecessary code duplication, since factories are often similar to one another. This may result in code that is difficult to maintain. Consider the case where object instantiation is configured by some outside resource like an XML file. If each factory must independently obtain information from this resource, then every time that the format of the XML file changes, every factory implementation must be modified as well. The service locator pattern addresses | One major drawback of using the factory pattern is that each abstraction needs its own factory. In this case, the only abstraction is thing, but if there were many abstractions this could lead to a lot of unnecessary code duplication, since factories are often similar to one another. This may result in code that is difficult to maintain. Consider the case where object instantiation is configured by some outside resource like an XML file. If each factory must independently obtain information from this resource, then every time that the format of the XML file changes, every factory implementation must be modified as well. | ||
The service locator pattern addresses the problem of factory proliferation by centralizing object instantiation. In a sense, the service locator consolidates a collection of factories into a single repository from which other objects may request dependencies. However, the role of the service locator is somewhat different from the factory. Whereas factories are specifically concerned with object creation, the service locator is concerned with supplying dependencies to objects. Consider several classes which all depend on a common abstraction. Whereas a factory would typically create an instance of the abstraction for each of the classes, the service locator might create a single instance and supply it to all of the classes. | |||
<pre> | <pre> | ||
class ServiceLocator | class ServiceLocator |
Revision as of 14:17, 3 November 2010
The Dependency Injection Pattern
An introduction by way of example
The dependency injection pattern is a design pattern for fully decoupling one class from the instantiation of another class upon which it depends. In this sense, it is similar to the factory and service locator patterns which are also concerned with object creation. The utility of and differences between these patterns will be demonstrated through example. Suppose, that Syd has a strategy for making statements about objects illustrated in the following Ruby code.
# A strategy for remarking on things. class Syd def remark if @thing.got_it? puts "I've got a #{@thing}." else puts "I know a #{@thing}." end end end
With this method, Syd can make a statement about any object thing that implements the methods got_it? and to_s. The Bike class shown below is an example.
# An example of a thing upon which to remark. class Bike def to_s "bike" end def got_it? return true end end
However, in order for Syd to use his strategy, he must get ahold of a thing. The simplest way to do this is to instantiate a thing directly.
# An example of direct object instantiation. class Syd def initialize @thing = Bike.new end end
However, now all that Syd can talk about is his bike. This is because, although Syd has a strategy for talking about a wide variety of things, Syd depends on the concrete class Bike for thing creation. A common technique for encapsulating object instantiation is the factory pattern. The following example uses a factory to allow Syd to talk about an array of things.
# An example of the factory pattern. class ThingFactory def initialize # An array of thing classes. @things = [Bike, Cloak, Mouse, GingerbreadMen, Room] end def create_random_thing # Pick a random thing and create an instance of it. @things.sort_by { rand }[0].new end end $thing_factory = ThingFactory.new class Syd def initialize # Use the factory to create a random thing @thing = $thing_factory.create_random_thing end end
One major drawback of using the factory pattern is that each abstraction needs its own factory. In this case, the only abstraction is thing, but if there were many abstractions this could lead to a lot of unnecessary code duplication, since factories are often similar to one another. This may result in code that is difficult to maintain. Consider the case where object instantiation is configured by some outside resource like an XML file. If each factory must independently obtain information from this resource, then every time that the format of the XML file changes, every factory implementation must be modified as well.
The service locator pattern addresses the problem of factory proliferation by centralizing object instantiation. In a sense, the service locator consolidates a collection of factories into a single repository from which other objects may request dependencies. However, the role of the service locator is somewhat different from the factory. Whereas factories are specifically concerned with object creation, the service locator is concerned with supplying dependencies to objects. Consider several classes which all depend on a common abstraction. Whereas a factory would typically create an instance of the abstraction for each of the classes, the service locator might create a single instance and supply it to all of the classes.
class ServiceLocator def initialize # Several arrays of abstractions @things = [Bike, Cloak, Mouse, GingerbreadMen, Room] @charms = [Heart, Star, Horseshoe, Clover] @triffles = [Tiramisu, StrawberryShortcake, TipsyLaird] end end # Add a create_random method for each abstraction [:thing, :charm, :trifle].each do |abstraction| ServiceLocator.class_eval "def create_random_#{abstraction} @#{abstraction}s.sort_by { rand }[0].new end" end $service_locator = ServiceLocator.new class Syd def initialize # Use the factory to create a random thing @thing = $service_locator.create_random_thing end end
However,