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