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 ??
> 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.
> 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