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#FormsOfDependencyInjection: "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: 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 |
Sorry I'm not familiar with the pattern, so I don't have a direct answer, and the following might not have a direct answer either, but in general I'd strongly suggest two books for adapting from static OO languages. 2. A Mentoring Course on Smalltalk, Valloud On Mon, Jun 5, 2017 at 5:17 AM, Vitor Medina Cruz <[hidden email]> wrote:
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'.
Just to satisfy my curiosity, I did find... "MVC was invented on Smalltalk and is arguably the original Inversion of Control framework. ... I think that IOC or the Dependency Injection pattern solves a problem that doesn't really exist in the Smalltalk environment. Smalltalk is an untyped dynamic language and uses message passing to communicate. This makes for objects that are loosely coupled by nature at the language level" And this... "In an explicitly-typed language like C++, C#, or Java, it is necessary to define an explicit interface for the injected collaborator. The real collaborator and the substitute both have to implement the interface. In a dynamically-typed language like Ruby [& Smalltalk], explicit interfaces are not necessary. We can just take advantage of 'duck-typing'." And GIlad Bracha knows a bit about Java... "I think that if you’re stuck with a mainstream language, [dependency Injection] may be a reasonable work around. It requires a significant degree of preplanning, and makes your application dependent on one more piece of machinery that has nothing to do with the actual problem the application is trying to solve. On the positive side, it helps guarantee employment for software engineers. That said, it’s important to understand that DIFs are just a work around for a deficiency in the underlying language. So why not get rid of constructors and have a class declaration create a factory object instead? Well, Smalltalk did just that a generation ago. Every time you define a class, you define the factory object for its instances." btw, following Randy's example the two steps #new & #finder: should hidden/combined by renaming MovieLister>>finder: to MovieLister>>initializeWithFinder: and adding... MovieLister class >> newWithFinder: finder ^ self basicNew initializeWithFinder: finder to be used like this... lister := MovieLister newWithFinder: (ColonDelimitedMovieFinder on: 'movies1.txt'). movies := lister moviesDirectedBy: 'Tarantino'. btw, while you are at it, if you haven't done this before, try implementing this using Smalltalk's version of TDD... In a clean image, first implement the following method and run the test, then fill code as needed just-in-time... MovieListerTest>>testColonDelimitedFinder lister := MovieLister newWithFinder: (ColonDelimitedMovieFinder on: 'movies1.txt'). movies := lister moviesDirectedBy: 'Tarantino'. self assert: (movies includes: 'Pulp Fiction') I see similar comments to my references in these. I hope the bits I've focussed on are not redundant .
Reading this, I think maybe all I've done with my example is echo this. So what I'm not clear on is what you mean by "wire things by hand"
I would say use Pharo's TDD approach of coding-from-within-the-debugger. The beauty of it is not that you get a lot of test coverage. There other significant benefits are: A. Your API ends up designed "in the most natural way to be used" since each time you code how you want to use your API before you define the implementation. B. You only need to code the minimum to get that API aspect working. So you get a faster feedback loop on your design. C. You consistency API implementation, you might define a test like... MovieListerTest>>testAllFinders AbstractMovieFinder allSubclasses do: [ :finderclass | lister := MovieLister newWithFinder: (finderclass on: 'movies1.txt'). movies := lister moviesDirectedBy: 'Tarantino'. self assert: (movies includes: 'Pulp Fiction'). ] and then each time you add an AbstractMovieFinder subclass, you implementation is again just work through fixing the tests from within the debugger.
TDD helps you avoid worrying about problems that might not exist. cheers -ben
|
In reply to this post by Vitor Medina Cruz
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 <[hidden email]> 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#FormsOfDependencyInjection: > > "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-dependency-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 > > > |
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. 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... and Stephane: Why don't you simply pass the class and use that class in your MovieLister? 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: 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 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 My assembler and service locator could be even more elaborated, and provide a different MovieFinder in test scope, for different classes or wharever. 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. If you do not want to have a reference at runtime to a Finder then you I didn't understand that, could you elaborate? On Mon, Jun 5, 2017 at 6:41 AM, Stephane Ducasse <[hidden email]> wrote: Why don't you simply pass the class and use that class in your MovieLister? |
On Tue, Jun 6, 2017 at 1:26 AM, Vitor Medina Cruz <[hidden email]> wrote:
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?
So per Fowler, the above is equivalent to "Setter Injection with Spring"
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
Assembler class>>configure ServiceLocator load: (ServiceLocator new movieFinder: (ColonMovieFinder newWith: 'movies1.txt') otherService: MyCustomService new)
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
|
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 <[hidden email]> wrote:
|
On Tue, Jun 6, 2017 at 5:11 AM, Stephane Ducasse <[hidden email]> wrote:
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
but I'll add, it was so much simpler to understand without all that Java typing boiler plate. cheers -ben
|
Thanks for the detailed answer ben :) I will try to clarify a little: the question is how to loosely wire objects, such as MovieLister to MovieFinder, so that changes in the wiring can be made without effort. In Java, people use DI containers and xml files or annotations to provide the wiring configuration, which is easy to switch entirely. For example, I could have a xml file for production code and another for testing purposes. Also, another advantage is that the container takes care of ordering object creation for me. For example, if the object A, B and C needed to be injected on the object Y, I just have to declare each one of those objects on the configuration file, the container arranges the creation order for me. I started, however, to question DI as a valid mechanisms because of it's complexities and other problems. The article from Fowler provides the service locator as an alternative which seems to me much simpler and completely fine solution for the problem. So, to answer you question "Is it any more complicated than that?": In the DI approach, yes it can be, but I don't think so in the service locator approach. I am asking here because I wanted to know how people from Smalltalk deal with this problem. As it seems there is no standard approach, nor this is perceived as a problem... You think it is enough to do the wiring by hand? It is ok to use a service locator approach? Or wouldn't you care about that? but I'll add, it was so much simpler to understand without all that Java typing boiler plate. Yeah, that's for sure! I am just used to read Java code. []s, Vitor On Mon, Jun 5, 2017 at 9:46 PM, Ben Coman <[hidden email]> wrote:
|
In reply to this post by Vitor Medina Cruz
I don't think using a DI container worth the effort. They add lots of complexities and solve very little. For some reason DI containers became very popular in the Java world, but if you take a look at other programming communities you'll realize that many people are perfectly happy without using these containers. Using DI and using a DI container is orthogonal. As you also said you can just pass dependencies to objects to achieve loose coupling. Yes, you have to do this manually but what's the big deal? We're talking about code with cyclomatic complexity of 1. Calling a constructor is not a problem that need to be solved. Using an external XML configuration to describe object wiring is the worst idea ever.
Here is an article about using plain old object composition to do DI http://blog.davidpeterson.co.uk/2011/01/object-oriented-example.html Some more thoughts about the problems associated with DI containers: http://www.natpryce.com/articles/000783.html http://higherorderlogic.com/2011/07/is-dependency-injection-like-facebook |
In reply to this post by Vitor Medina Cruz
On Tue, Jun 6, 2017 at 9:11 PM, Vitor Medina Cruz <[hidden email]> wrote:
I don't see what is special about this. You can easily arrange instance creation order with methods on the class-side of your domain classes. Indeed, the GTTools are set up to work with in-Image sample data. Look at implementors of #sample and #example. There was quite some bike-shedding over the naming convention (and I forget the final result), but hopefully it provide the general idea... http://forum.world.st/Existing-lt-script-gt-lt-example-gt-pragmas-and-new-GT-needs-td4814903i20.html
If it seems suitable, then to quote Nike, just do it ;)
DI or Service Locator are both "implementations" of your need. Can we take step backward to see what is your need? To kick off, I hazard a guess at some possible needs... 1. To switch between configurations to use production data and test data ? 2. To make this switch during CI testing and production deployment ? 3. To switch from the command line ? 4. Want the configuration to editable remotely? e.g. from a text editor? ? ...
I'm having trouble understanding this "by hand" concept. Don't you create the xml configuration file by hand? You can just as easily
If it works, its okay. So very generally, I'd avoid starting with any external test data, so as to not distract myself by that implementation. I'd copy-paste some test data into the class-side-methods of the domain classes and hack some instance creation code around them. Build your first tests around those. Then later, consolidate them via an InImageTestData object that you'll later replace with ExternalTestData
As an aside, try evaluting "Smalltalk tools inspect" and debugging "Smalltalk tools browser open" cheers -ben |
> I don't see what is special about this. You can easily arrange instance
> creation order with methods on the class-side of your domain classes. I will not use the term "class", but rather a "service" (service can be just a single class, but that is not always the case). The point of inversion of control is provide the class with what is needed instead of the class creating it on it's own. Sure, you can create class-side helper, however that is very limited, such as the examples/samples as you've demonstrated. To give you an example, imagine that you have a class PeopleService that manages database of people, so it needs a connection to the database. Implemented in a naive way the class would have hard-coded credentials to a MySQL database and it would execute raw SQL queries. This works for a single class, but it creates a mess and mingles concerns; so instead you inverse the control and you provide PeopleService with e.g. a Persistence instance, which is already connected to the database; note however that you cannot just create the Persistence on the class-side, because you wouldn't even know what to instantiate (as you could e.g. use DbPersistence, FilePersistence, ...). And this is just a basic example. Imagine that the PeopleService also needs other services for it's use, and those services need more services, and you want to keep everything separated as much as possible... instantiation by hand will simply not cut it So the service locator/DI configuration (whether as annotations or in a XML file) keeps the actual classes separated from concrete instances; the service depends on abstract interfaces, not specific classes; only the conf determines the specific ones, and more often than not there will be several different configurations _at the same time_. To continue with the example you could have one config production using e.g. OraclePersistence, another for development using MySQLPersistance, another for test server using MemoryPersistence, etc. (also with different credentials, and whatnot). Keep in mind however that relates primarily to using application frameworks, where the framework mandates the control, and the framework is the one calling you, and therefore it will also provide you with necessary parts. After all you are not even creating PeopleService class, the framework instantiates it for you and gives it to another services/classes that needed it. Now I don't feel like there are many application frameworks in Pharo, maybe Seaside has something of this sorts? I guess Spec can be a bit framework-y, as the basic spec widgets do not actually know what Morphic counterparts will be created for them, and Spec looks up the actual class in a hard-coded data table (which might as well be a XML file), but that comes closer to ServiceLocator than DI (as Spec widgets should not actually communicate down to morphic). I've used dependency injection for five years at my previous work and frankly I cannot imagine living without it there, but I've never felt the need for it in Pharo. This is probably more related to working in Pharo on completely different things and writing small libraries. However I wouldn't be surprised if the liveness of Smalltalk/Pharo environment eliminated some of the problems that DI is trying to solve. But then again, there are many ways to build systems, and not all need or benefit DI/IoC. Peter > Indeed, the GTTools are set up to work with in-Image sample data. Look at > implementors of #sample and #example. > There was quite some bike-shedding over the naming convention (and I forget > the final result), but hopefully it provide the general idea... > > http://forum.world.st/a-request-to-change-the-meaning-of-lt-example-gt-pragma-td4927360.html > http://forum.world.st/lt-example-gt-lt-examplar-gt-td4911728i20.html > http://forum.world.st/Existing-lt-script-gt-lt-example-gt-pragmas-and-new-GT-needs-td4814903i20.html > > > > > > > I started, however, to question DI as a valid mechanisms because of it's > > complexities and other problems. The article from Fowler provides the > > service locator as an alternative which seems to me much simpler and > > completely fine solution for the problem. > > > > If it seems suitable, then to quote Nike, just do it ;) > > > > > So, to answer you question "Is it any more complicated than that?": In > > the DI approach, yes it can be, but I don't think so in the service locator > > approach. > > > > I am asking here because I wanted to know how people from Smalltalk deal > > with this problem. As it seems there is no standard approach, nor this is > > perceived as a problem... > > > > > DI or Service Locator are both "implementations" of your need. Can we take > step backward to see what is your need? To kick off, I hazard a guess at > some possible needs... > > 1. To switch between configurations to use production data and test data ? > 2. To make this switch during CI testing and production deployment ? > 3. To switch from the command line ? > 4. Want the configuration to editable remotely? e.g. from a text editor? > ? > ... > > > > > You think it is enough to do the wiring by hand? > > > > I'm having trouble understanding this "by hand" concept. > Don't you create the xml configuration file by hand? > You can just as easily > > > > > It is ok to use a service locator approach? Or wouldn't you care about > > that? > > > > If it works, its okay. > > So very generally, I'd avoid starting with any external test data, so as to > not distract myself by that implementation. > I'd copy-paste some test data into the class-side-methods of the domain > classes and hack some instance creation code around them. > Build your first tests around those. Then later, consolidate them via an > InImageTestData object that you'll later replace with ExternalTestData > > > > > > > > > but I'll add, it was so much simpler to understand without all that Java > >> typing boiler plate. > > > > > > Yeah, that's for sure! I am just used to read Java code. > > > > []s, > > Vitor > > > > > > > As an aside, try evaluting "Smalltalk tools inspect" > and debugging "Smalltalk tools browser open" > > cheers -ben |
In reply to this post by Attila Magyar
On Tue, Jun 6, 2017 at 11:48 PM, Attila Magyar <[hidden email]> wrote: I don't think using a DI container worth the effort. They add lots of I liked this... "the [Dependency Injection] pattern also used to be called "third-party binding" or "third-party connect": some third party is responsible for connecting an object satisfying the service requirements of a component" This makes the subject seem less esoteric. It reminds me of hearing that the first (secret) task when doing a PhD is to invent new terms for common ones, and base your writings on that. Perhaps its the same for consultants and framework developers. ;P Or maybe everything seems esoteric until you have experience with it and I've not done much with Java, certainly not big applications. Thx Peter for your example and how you prior experience compares to Pharo. cheers -ben
|
Thanks for the links Attila, do you have an alternative one to the http://higherorderlogic.com/ I also started to also think containers don't worth the effort, but I am still concerned with problems faced with hand wiring everything as Peter clearly exemplified, thanks for the excellent summary and exemplification Peter! And this is just a basic example. Imagine that the PeopleService also needs other services for it's use, and those services need more services, and you want to keep everything separated as much as possible... instantiation by hand will simply not cut it This is exactly my concern! Sure, for small stuff that is not a problem, but what if you have a big app? Now, my experience with containers and Java may be shadowing my vision: people in Java community tend to use anemic model and to create gazzilions layers for even simple stuff, which make the wiring problem even worse. My experiments with a more OO approach and Smalltalk tells me one would need much less need of containers by using proper OO and a less verbose language, but I am not sure. Keep in mind however that relates primarily to using application frameworks, where the framework mandates the control, and the framework is the one calling you, and therefore it will also provide you with necessary parts. After all you are not even creating PeopleService class, the framework instantiates it for you and gives it to another services/classes that needed it. This is true for IoC DI containers, but I don't think it applies for service locator, which, I think, could not be considered a framework approach. Actually, I think being a framework is the big problem of the of the former, since when I start to use an IoC container to create objects I got tied to it in a very basic and pervasive operation of an OO system: object creation. Suddenly everything must be created through the DI container, but that is not true for Service Locator. However I wouldn't be surprised if the liveness of Smalltalk/Pharo environment eliminated some of the problems that DI is trying to solve. That is precisely what I wanted to know. It seems there are no DI counterpart in the Pharo/Smalltalk environment, ok, so what people do to to deal with the above mentioned problem? Hand wiring is suffice? Is there anything in the liveness of Smalltalk/Pharo that eliminates the need for it? People use simple Service Locator, or variations of it? cheers! Vitor On Wed, Jun 7, 2017 at 9:32 PM, Ben Coman <[hidden email]> wrote:
|
In reply to this post by Vitor Medina Cruz
Also realize the Smalltalk image is a container... No need for fancy XML externalization because you can do that with code. I have an app where I wire all kinds of things together in a configuration file which is Smalltalk code and I just compile a method like "wireEverything" that as source has that configuration file. Or just do a Compiler evaluate: aLongConfigurationStringInSmalltalk Any Dictionary can be a "SpringContext" thing. Now decorators with specific features like transaction support etc is not something we have and things like Spring do have. We could take inspiration from Spring Boot. Best Regards, Phil On Thu, Jun 8, 2017 at 1:46 PM, Vitor Medina Cruz <[hidden email]> wrote:
|
In reply to this post by Vitor Medina Cruz
I don't think it makes any difference. You have to decide in each case whether an object is an internal (implementation detail) and therefore it must be created from inside the class that uses it, or a dependency (or peer) that should be passed as an argument. In the later case you instantiate the object from the outside and hand it over to the object that requires it. Most applications have a few major top level abstractions. Let's call them subsystems. I would use more domain specific names in a concrete case but this is a general example. These subsystems are instantiated at the entry point of the application. If there are shared dependencies between them, those are instantiated here as well. Then each subsystem can build up some object graph that is required by the actual subsystem (Not everything needs to know about everything else. There are clusters of objects that represent domain level abstractions). The same things applies here as well. Some dependencies are passed through (peers), others are instantiated (internals) from the inside. This can go on and on further and it will result a fractal like structure. The whole application is one object. It consists of few other objects. Those object consists of further objects. In a typical Spring application there is big blob of object soup with full of equivalent objects, everyone can access to everything else and this fractal structure is missing. |
In reply to this post by Vitor Medina Cruz
On Thu, Jun 08, 2017 at 08:46:15AM -0300, Vitor Medina Cruz wrote:
> This is true for IoC DI containers, but I don't think it applies for > service locator, which, I think, could not be considered a framework > approach. Actually, I think being a framework is the big problem of the of > the former, since when I start to use an IoC container to create objects I > got tied to it in a very basic and pervasive operation of an OO system: > object creation. Well that is the objective of DI to handle the object creation on your behalf. Of course no class is prohibited from creating instances directly, provided it had the necessary dependencies to give to the instance. > Suddenly everything must be created through the DI container, but that is not true for Service Locator. As I've noted above, DI doesn't restrict you from creating instances. Service Locator gives you a more flexible approach, however it follows that it can be also harder to manage, as the services registered in the locator can (and typically do) change at runtime, which inhibits static reasoning about the dependencies, compared to a pre-fixed configuration. > > However I wouldn't be surprised if the liveness of Smalltalk/Pharo > > environment eliminated some of the problems that DI is trying to solve. > > > That is precisely what I wanted to know. It seems there are no DI > counterpart in the Pharo/Smalltalk environment, ok, so what people do to to > deal with the above mentioned problem? Hand wiring is suffice? Is there > anything in the liveness of Smalltalk/Pharo that eliminates the need for > it? People use simple Service Locator, or variations of it? In my previous mail I've mentioned Spec doing something of the sorts, but I don't think I've seen it anywhere else. The hand-wiring mostly suffices; sometimes you can see having a method containing the name of the class it will instantiate (and which can be altered in a subclass). My best guess would be that the capabilities of Pharo, such as pluggable closures, and being a live already-running system are more likely to push the programmer towards a different architecture, where incidentally DI's are not such a concern, but I would certainly love to see some deep analysis on this. Peter > > > cheers! > Vitor > > On Wed, Jun 7, 2017 at 9:32 PM, Ben Coman <[hidden email]> wrote: > > > > > > > On Tue, Jun 6, 2017 at 11:48 PM, Attila Magyar <[hidden email]> > > wrote: > > > >> I don't think using a DI container worth the effort. They add lots of > >> complexities and solve very little. For some reason DI containers became > >> very popular in the Java world, but if you take a look at other > >> programming > >> communities you'll realize that many people are perfectly happy without > >> using these containers. Using DI and using a DI container is orthogonal. > >> As > >> you also said you can just pass dependencies to objects to achieve loose > >> coupling. Yes, you have to do this manually but what's the big deal? We're > >> talking about code with cyclomatic complexity of 1. Calling a constructor > >> is > >> not a problem that need to be solved. Using an external XML configuration > >> to > >> describe object wiring is the worst idea ever. > >> > >> Here is an article about using plain old object composition to do DI > >> > >> http://blog.davidpeterson.co.uk/2011/01/object-oriented-example.html > >> > >> Some more thoughts about the problems associated with DI containers: > >> > >> http://www.natpryce.com/articles/000783.html > > > > > > I liked this... "the [Dependency Injection] pattern also used to be > > called "third-party binding" or "third-party connect": some third party is > > responsible for connecting an object satisfying the service requirements of > > a component" > > This makes the subject seem less esoteric. It reminds me of hearing that > > the first (secret) task when doing a PhD is to invent new terms for common > > ones, and base your writings on that. Perhaps its the same for consultants > > and framework developers. ;P Or maybe everything seems esoteric until you > > have experience with it and I've not done much with Java, certainly not big > > applications. > > > > Thx Peter for your example and how you prior experience compares to Pharo. > > > > cheers -ben > > > > > >> http://higherorderlogic.com/2011/07/is-dependency-injection-like-facebook > >> > >> > >> > >> -- > >> View this message in context: http://forum.world.st/Wiring-o > >> bjects-IoC-and-Service-Locator-tp4949280p4949720.html > >> Sent from the Pharo Smalltalk Users mailing list archive at Nabble.com. > >> > >> > > |
In reply to this post by Attila Magyar
On Thu, Jun 08, 2017 at 08:02:04AM -0700, Attila Magyar wrote:
> Vitor Medina Cruz wrote > > This is exactly my concern! Sure, for small stuff that is not a problem, > > but what if you have a big app? > > I don't think it makes any difference. Scale can make all the difference. Imagine the complexity of connecting N elements. If you have just tree objects/classes/systems... you need just two connections, but you if double the number of elements, you need more than seven times more connections (to form a complete graph). Of course in practice that number will be smaller, but certainly more than linear to the number of elements. So what works in small scale doesn't necessarily translate to bigger scale, not to mention that brand new concerns are probably going to arise... just like when you are constructing a taller building, you cannot just slap it twice on each other, because the foundations will collapse, and when you want to build a space elevator, you need technology that hasn't been invented yet. |
I don't think it should. If you have a small cluster of objects where you can manage the dependencies yourself, you can compose larger objects from them and manage the dependencies the same way. By doing so, you'll get an object that is simpler from the outside than it's parts in the inside, and you'll need to solve the smaller problem again. In other words we shouldn't write large software, but compose large software from smaller ones. But the same can be done with objects (that's why I'm not a big fan of microservices despite they solve this problem similarly). If you have a flat structure (where objects don't form higher level abstractions) and everything needs to know about everything else, then the DI container will make it easier to work with this structure. But I don't think this is the right way to organize a software. |
In reply to this post by Vitor Medina Cruz
In SmallTalk:-
1. Blocks (i.e. anonymous functions) means that one needs not necessarily implement a Class (MovieFinder), in order to vary alternative behavior, unlike Java (before 8). 2. If one does want a number of "finders" (as Classes), then one will typically see one "injected" (constructor or method injection), where each understands some common "find" message. Is the real question for 2 above, "what (or who) decides" which will be injected? |
Free forum by Nabble | Edit this page |