On Tue, Jun 6, 2017 at 5:11 AM, Stephane Ducasse <stepharo.s...@gmail.com>
wrote:

> Tx ben.
> When I see all this complexity for something that looks not that complex:
> I prefer to pass the class and get done.
> May be I missed something obvious... but when I see something too complex
> I start to get worried.
>
> I think that thinking about the contract between classes at runtime is
> important and to me a MovieLister should be using at runtime and instance
> of the *Finder*
> Now needing an extra class just to set this should really be evaluated
> with the tradeoff: "flexibility win" (our simple solution is still really
> flexible - I decide when I want to pass the correct finder) vs. the code
> and conceptual bloat.
>
>
> I'm happy not to face the hyper super over engineering of Java "solutions".
>
> I like the "Of course this just shifts the burden a tad, we still have to
> get the locator into the lister"
> but the solution is super simple let us use another "Singleton and a
> Factory...." :)
>
> Stef
>
> On Mon, Jun 5, 2017 at 8:43 PM, Ben Coman <b...@openinworld.com> wrote:
>
>>
>>
>> On Tue, Jun 6, 2017 at 1:26 AM, Vitor Medina Cruz <vitormc...@gmail.com>
>> wrote:
>>
>>> Thanks for the answer Ben and Stephane.
>>>
>>> I already read A Mentoring Course on Smalltalk, Valloud, there is
>>> nothing there I could use in this case :( . I will look after for The
>>> Design Patterns Smalltalk Companion. Most of the sources provided I already
>>> know of or went in the same lines lines of what I have already found.
>>>
>>> About TDD, I am experienced with the discipline and have tested it on
>>> Pharo living system already, but I could not understand how this is related
>>> with object wiring, DI and service locator.
>>>
>>
>> I guess I don't properly understand your need and those topics.  That was
>> my quick pass.  Now if I take the time to actually read Fowler's long
>> article
>>
>> "the inversion is about how they lookup a plugin implementation ... to
>> ensure that any user of a plugin follows some convention that allows a
>> separate assembler module to inject the implementation into the lister."
>>
>> "The basic idea of the Dependency Injection is to have a separate object,
>> an assembler, that populates a field in the lister class with an
>> appropriate implementation for the finder interface. There are three main
>> styles of dependency injection. The names I'm using for them are
>> Constructor Injection, Setter Injection, and Interface Injection."
>>
>> Now there was too much syntactical noise in those Java examples for me to
>> think clearly, so I converted them all to Smalltalk.
>>
>>
>> ##CONSTRUCTOR INJECTION
>>
>> Object subclass: MovieLister
>>     instanceVariables: 'finder'
>>
>>
>> MovieLister class >> newWith: aFinder
>>     ^ self basicNew initializeWith: aFinder
>>
>> MovieLister >> initializeWith: aFinder
>>     finder := aFinder
>>
>>
>> ColonMovieFinder class >> newWith: aFilename
>>     ^ self basicNew initializeWith: aFilename
>>
>> ColonMovieFinder >> initializeWith: aFilename
>>     filename := aFilename.
>>
>>
>> ConstructorInjectionContainer >> new
>>     container := DefaultContainer new.  "the article doesn't specify
>> where this comes from"
>>     finderParams := ConstantParameter newWith: 'movies1.txt'.
>>     container registerComponentInterface: MovieFinderInterface
>>                           implementation: ColonMovieFinder
>>                           params: finderParams.
>>     container registerComponentImplementation: MovieLister
>>     ^container
>>
>> to be used like this...
>> ConstructorInjectionTest >> testWithContainer
>>     container := ConstructorInjectionContainer new.
>>     lister := container getComponentInstance( MovieLister ).
>>     movies = lister moviesDirectedBy: 'Sergio Leone'.
>>     self assert: (movies includes: 'Once Upon a Time in the West')
>>
>> The article poorly defines registerComponentXXX: or getComponentInstance:
>> methods, so I don't dwell on them.  I presume its little relevant to the
>> main theme.
>>
>>
>> ##SETTER INJECTION
>>
>> MovieLister >> setFinder: aFinder
>>     finder := aFinder.
>>
>> ColonMovieFinder >> setFilename: aFilename
>>     filename := aFilename.
>>
>> SetterInjectionTest >> testWithConfigurationFile
>>     ctx := SomeXmlApplicationConfiguration on: 'config.xml'.
>>     lister := ctx getConfigOf: 'MovieLister'.
>>     movies = lister moviesDirectedBy: 'Sergio Leone'.
>>     self assert: (movies includes: 'Once Upon a Time in the West')
>>
>>
>> ##INTERFACE INJECTION
>>
>> MovieLister >> injectFinder: aFinder
>>     finder := aFinder
>>
>> ColonMovieFinder >> injectFilename: aFilename
>>     filename := aFilename
>>
>> InterfaceInjectionTest >> configureContainer
>>     container := InterfaceInjectionContainer new.
>>     self registerComponents.
>>     self registerInjectors.
>>     container start.
>>
>> InterfaceInjectionTest >> registerComponents
>>     container registerComponent: 'MovieLister' with: MovieLister.
>>     container registerComponent: 'MovieFinder' with: ColonMovieFinder.
>>
>> InterfaceInjectionTest >> registerInjectors
>>     container registerInjector: Injector  with: (container lookup:
>> 'MovieFinder').
>>     container registerInjector: InjectorFinderFilename  with:
>> FinderFilenameInjector new.
>>
>> ColonMovieFinder >> inject: anObject
>>     anObject injectFinder: self.
>>
>> FinderFilenameInjector >> inject: anObject
>>     anObject injectFilename: 'movies1.txt'.
>>
>> InterfaceInjectionTester >> testInterface
>>     self configureContainer.
>>     lister := container lookup: 'MovieLister'.
>>     movies = lister moviesDirectedBy: 'Sergio Leone'.
>>     self assert: (movies includes: 'Once Upon a Time in the West')
>>
>> The article doesn't define InterfaceInjectionContainer, but I guess it
>> could look like this...
>>
>> InterfaceInjectionContainer >> registerComponent: componentName with:
>> aComponent
>>     container ifNil: [ container := Dictionary new].
>>     container at: componentName put: aComponent
>>
>> InterfaceInjectionContainer >> lookup: componentName
>>     ^ container at: componentName
>>
>>
>> ##SERVICE LOCATOR
>>
>> "The basic idea behind a service locator is to have an object that knows
>> how to get hold of all of the services that an application might need. So a
>> service locator for this application would have a method that returns a
>> movie finder when one is needed. Of course this just shifts the burden a
>> tad, we still have to get the locator into the lister"
>>
>> MovieLister >> initialize
>>     finder := ServiceLocator movieFinder.
>>
>>
>> Object subclass: ServiceLocator
>>     instanceVariable: 'movieFinder'
>>     classVariable: 'SoleInstance'
>>
>> ServiceLocator class >> load: aServiceLocator
>>     SoleInstance := aServiceLocator
>>
>> ServiceLocator class >> soleInstance
>>     ^ SoleInstance
>>
>> ServiceLocator class >> movieFinder
>>     ^ self soleInstance movieFinder
>>
>> ServiceLocator >> movieFinder
>>     ^movieFinder
>>
>>
>> ServiceLocatorTest >> configure
>>     ServiceLocator load: (ServiceLocator newWith: (ColonMovieFinder
>> newWith: 'movies1.txt'))
>>
>>
>> ServiceLocator class >> newWith: aMovieFinder
>>     ^ self basicNew initializeWithFinder: aMovieFinder
>>
>> ServiceLocator >> initializeWithFinder: aMovieFinder
>>     movieFinder := aMovieFinder
>>
>>
>> ServiceLocatorTest >> testSimple
>>     self configure.
>>     lister := MovieLister new.
>>     movies = lister moviesDirectedBy: 'Sergio Leone'.
>>     self assert: (movies includes: 'Once Upon a Time in the West')
>>
>> So it seems that a service locator is just a Singleton pattern having a
>> class variable for each service of interest ??
>>
>
It was late and I mispoke here, of course this should have said...
* a Singleton pattern having an instance variable for each service of
interest
* a Singleton pattern having a getter method (with hidden implementation) for
each service of interest



> So in good faith** I ask... "Is it any more complicated than that?"
>>
>> **Since its taken me a couple of hours to convert the Java to this point
>> so I stopped reading to seek your feedback.
>>
>
but I'll add, it was so much simpler to understand without all that Java
typing boiler plate.

cheers -ben


> Is that enough insight to adapt to your needs, or is there something else
>> further down the article that invalidates my analysis?
>>
>>
>>
>>
>>
>>>
>>> From ben:
>>>
>>> "I'm not really familiar with IoC or DI patterns, so just taking your
>>>> example at face value, in Pharo I'd do...
>>>>
>>>> MovieLister>>moviesDirectedBy: director
>>>>     allMovies := finder allMovies.
>>>>     ^ allMovies select: [ :movie | movie getDirector = director ].
>>>>     "although typically #getDirector would be renamed #director"
>>>>
>>>> MovieLister>>finder: movieFinder
>>>>     finder := movieFinder.
>>>>
>>>> to be used like this...
>>>>     lister := MovieLister new finder: (ColonDelimitedMovieFinder on:
>>>> 'movies1.txt').
>>>>     movies := lister moviesDirectedBy: 'Tarantino'."
>>>
>>>
>>
>> So per Fowler, the above is equivalent to "Setter Injection with Spring"
>>
>>
>>>
>>> and Stephane:
>>>
>>> Why don't you simply pass the class and use that class in your
>>>> MovieLister?
>>>>
>>>> MovieLister new
>>>>      finderClass: MySuperCoolFinderClass
>>>>
>>>> ...
>>>> MovieLister finder
>>>>       finderClass new .....
>>>>
>>>> What is wrong with that.
>>>
>>>
>>> That was what I meant when I said: "I know that in Smalltalk I can make
>>> MovieLister to receive, upon construction, a class representing MovieFinder
>>> and call it construction message.". The code I had in mind is a bit of
>>> mix from the one provided by you both:
>>>
>>> MovieLister>>moviesDirectedBy: director
>>>     allMovies := finder allMovies.
>>>     ^ allMovies select: [ :movie | movie getDirector = director ].
>>>     "although typically #getDirector would be renamed #director"
>>>
>>> MovieLister>>finder: aMovieFinderBuilder
>>>     finder := aMovieFinderClass new.
>>>
>>> to be used like this...
>>>     lister := MovieLister new finder: (ColonDelimitedMovieFinder
>>> builderOn: 'movies1.txt').
>>>     movies := lister moviesDirectedBy: 'Tarantino'."
>>>
>>> But that means I will have to wire dependencies by hand whenever I
>>> create a MovieLister and seek through code when and if those dependencies
>>> change. When there are lot's of dependencies it's is a considerable and
>>> tedious work. Let's see an image from Fowlers article:
>>>
>>> [image: Inline image 1]
>>>
>>> In this case, the service locator provides me with an instance and I
>>> configure the instance in the assembler, the scheme is alike for an IoC,
>>> and that would mean my implementation could be like this:
>>>
>>>
>>> MovieLister>>moviesDirectedBy: director
>>>     allMovies := finder allMovies.
>>>     ^ allMovies select: [ :movie | movie getDirector = director ].
>>>     "although typically #getDirector would be renamed #director"
>>>
>>> MovieLister>>initialize
>>>     finder := ServiceLocator locate: FinderClass   <--- This would
>>> bring the instance of finder class configured by the assembler
>>>
>>
>> No, like this...
>>     finder := ServiceLocation movieFinder.
>>
>> Now if you want to store a class rather than an instance as I did higher
>> up, you just do this...
>>
>> Object subclass: ServiceLocator
>>     instanceVariable: 'movieFinderClass'
>>     classVariable: 'SoleInstance'
>>
>> ServiceLocator class >> movieFinder
>>     ^ self soleInstance movieFinderClass new
>>
>>
>>
>>>
>>>
>>> to be used like this...
>>>     lister := MovieLister new.
>>>     movies := lister moviesDirectedBy: 'Tarantino'."
>>>
>>> and the assembler:
>>>
>>> Assember class>>configure:
>>> aMap put: (ColonDelimitedMovieFinder builderOn: 'movies1.txt') at:
>>> FinderClass
>>>
>>
>> Assembler class>>configure
>>     ServiceLocator load:
>>           (ServiceLocator new
>>                movieFinder: (ColonMovieFinder newWith: 'movies1.txt')
>>                otherService: MyCustomService new)
>>
>>
>>>
>>> My assembler and service locator could be even more elaborated, and
>>> provide a different MovieFinder in test scope, for different classes or
>>> wharever.
>>>
>>
>> Really, the test should not be updating the class variable global like
>> this...
>> ServiceLocatorTest >> configure
>>     ServiceLocator load: (ServiceLocator newWith: (ColonMovieFinder
>> newWith: 'movies1.txt'))
>>
>> you probably want something like (its rough but shows the point...)
>> Object subclass: #MovieLister
>>     instanceVariables: 'serviceLocator'
>>
>> MovieLister >> initialize
>>     serviceLocator ifNil: [ serviceLocator := ServiceLocator soleInstance
>> ].
>>     finder := serviceLocator movieFinder.
>>
>> MovieLister class >> newWithServiceLocator: aServiceLocator
>>     ^ (self basicNew initializeWithServiceLocator: aServiceLocator)
>> initialize.
>>
>> MovieLister >> initializeWithServiceLocator: aServiceLocator
>>     serviceLocator := aServiceLocator
>>
>> ServiceLocatorTest >> testSimple2
>>     lister := MovieLister newWithServiceLocator: (ServiceLocator newWith:
>> (ColonMovieFinder newWith: 'movies1.txt')).
>>     movies = lister moviesDirectedBy: 'Sergio Leone'.
>>     self assert: (movies includes: 'Once Upon a Time in the West')
>>
>>
>> cheers -ben
>>
>>
>>>
>>> It is a little convenience for Smalltalk, I will give that, but I was
>>> wandering if there was something alike in Pharo, by your answers I assuming
>>> there is nothing like that.
>>>
>>>
>>>
>>> On Mon, Jun 5, 2017 at 6:41 AM, Stephane Ducasse <
>>> stepharo.s...@gmail.com> wrote:
>>>
>>>> Why don't you simply pass the class and use that class in your
>>>> MovieLister?
>>>>
>>>> MovieLister new
>>>>      finderClass: MySuperCoolFinderClass
>>>>
>>>> ...
>>>> MovieLister finder
>>>>       finderClass new .....
>>>>
>>>> What is wrong with that.
>>>>
>>>> If you do not want to have a reference at runtime to a Finder then you
>>>> need to use announcement and registration.
>>>>
>>>> Stef
>>>>
>>>>
>>>>
>>>> On Sun, Jun 4, 2017 at 11:17 PM, Vitor Medina Cruz <
>>>> vitormc...@gmail.com> wrote:
>>>> > Hello,
>>>> >
>>>> > I would like to know how people in Pharo ecosystem do to deal with
>>>> object
>>>> > wiring, as described by Marting Fowler in
>>>> > https://martinfowler.com/articles/injection.html#FormsOfDepe
>>>> ndencyInjection:
>>>> >
>>>> > "A common issue to deal with is how to wire together different
>>>> elements: how
>>>> > do you fit together this web controller architecture with that
>>>> database
>>>> > interface backing when they were built by different teams with little
>>>> > knowledge of each other."
>>>> >
>>>> > He gives an example, I will leave it in java as it is simple enough to
>>>> > understand:
>>>> >
>>>> > "class MovieLister...
>>>> >
>>>> >   public Movie[] moviesDirectedBy(String arg) {
>>>> >       List allMovies = finder.findAll();
>>>> >       for (Iterator it = allMovies.iterator(); it.hasNext();) {
>>>> >           Movie movie = (Movie) it.next();
>>>> >           if (!movie.getDirector().equals(arg)) it.remove();
>>>> >       }
>>>> >       return (Movie[]) allMovies.toArray(new Movie[allMovies.size()]);
>>>> >
>>>> >   }"
>>>> >
>>>> > The question is how to provide the finder object in a decoupled
>>>> matter, a
>>>> > naive approach would be:
>>>> >
>>>> > " private MovieFinder finder;
>>>> >
>>>> >   public MovieLister() {
>>>> >     finder = new ColonDelimitedMovieFinder("movies1.txt");
>>>> >
>>>> >   }"
>>>> >
>>>> > Which couples the MovieLister to the specific
>>>> ColonDelimitedMovieFinder
>>>> > class.
>>>> >
>>>> > Fowler explains how to decouple using an IoC framework or a Service
>>>> Locator.
>>>> > In Java and .Net IoC is used most of the time. I Googled how this
>>>> problem is
>>>> > approached in Smalltalk/Pharo, and I generally I found answers "that
>>>> is easy
>>>> > to do in Smalltalk, so there is no need of a framework", what I miss
>>>> is a
>>>> > description on *how* to do that:
>>>> >
>>>> > https://stackoverflow.com/questions/243905/smalltalk-and-ioc
>>>> > https://stackoverflow.com/questions/2684326/is-there-a-depen
>>>> dency-injection-framework-for-smalltalk
>>>> > https://stackoverflow.com/questions/243905/smalltalk-and-ioc
>>>> /347477#347477
>>>> >
>>>> > I know that in Smalltalk I can make MovieLister to receive, upon
>>>> > construction, a class representing MovieFinder and call it
>>>> construction
>>>> > message. As long an object that responds to this message is provided,
>>>> I can
>>>> > create as many derivations I want and the MovieLister will be
>>>> decoupled from
>>>> > the MovieFinder. That way, however, I still have to wire things by
>>>> hand, and
>>>> > I am not sure if this is what I am supposed to do in order to solve
>>>> the
>>>> > decouple problem.
>>>> >
>>>> > Can you explain me how this is done in Pharo? It's is usually wiring
>>>> by
>>>> > hand? Is there a simple construction that deals with the wiring
>>>> problem that
>>>> > I cannot foresee?
>>>> >
>>>> > Thanks in advance,
>>>> > Vitor
>>>> >
>>>> >
>>>> >
>>>>
>>>>
>>>
>>
>

Reply via email to