From: Andrea Fazzi Date: 2008-06-26T01:04:13+09:00 Subject: Re: Is it ellegant to use a global variable to store a Logger object? Robert Dober ha scritto: > On Wed, Jun 25, 2008 at 3:36 PM, Andrea Fazzi wrote: > >> Robert Dober ha scritto: >> >>> On Wed, Jun 25, 2008 at 11:22 AM, Robert Klemme >>> wrote: >>> >>> >>>> 2008/6/24 I�aki Baz Castillo : >>>> >>>> >>>>> El Martes, 24 de Junio de 2008, Andrea Fazzi escribi�: >>>>> >>>>> >>>>>> require 'logger' >>>>>> >>>>>> class Container >>>>>> def logger >>>>>> @logger ||= Logger.new STDOUT >>>>>> end >>>>>> def foo >>>>>> Foo.new(logger) >>>>>> end >>>>>> end >>>>>> >>>>>> class Foo >>>>>> def initialize(logger) >>>>>> @logger = logger >>>>>> end >>>>>> def bar >>>>>> @logger.info('Foo#bar invoked.') >>>>>> end >>>>>> end >>>>>> >>>>>> c = Container.new >>>>>> c.foo.bar >>>>>> >>>>>> >>>>> That's a cool solution :) >>>>> >>>>> >>>> I do not think so. Reasons: it clutters every instance with a >>>> reference which can have a significant impact on memory if there are a >>>> lot objects. Then, you have to change a class's #initialize signature >>>> for all classes that do want to do logging and also manually code the >>>> assignment inside the class. Accessing a logger from a global context >>>> (whichever way you do it) saves memory and is less tedious. >>>> >>>> Kind regards >>>> >>>> robert >>>> >>>> >>>> >>>> -- >>>> use.inject do |as, often| as.you_can - without end >>>> >>>> >>>> >>>> >>> Could not agree more with you. May I add some other reasons. >>> This approach vioaltes principles we regard rather highly on this list >>> It is not DRY, you are really repeating yourself and in case of the >>> metaprogramming solutions you let Ruby repeat itself. >>> It is just much less simple than necessary. >>> You spread code dependencies all over the place, as a matter of fact >>> the expression "dependency injection" says it allready it is almost as >>> putting a virus (with constructive behavior) into your code, but can >>> you imagine how much more work refactoring will become? >>> >>> My order of preference would be >>> 1. $logger or $LOGGER >>> 2. Logger >>> >>> 43. Kernel::log (or Object.log) >>> >>> Cheers >>> >>> >>> >>> >>> >>> >> Hi Robert, >> >> I would known if, in your opinion, DI is not DRY in general or you are >> referring to the particular case of logging. Moreover, which are alternative >> DRY solutions to DI that guarantees loose coupling between objects? >> >> Andrea >> >> >> >> > I am by no means qualified to judge DIin general and I have to admit > that my wording might have been indeed to offensive. Scusa per questo. > Don't worry :-) > It is however intriguing to me when dependency injection is a good > solution. I am sure that there is plenty space for its application, > maybe even for logging in a context more specific than the one given > by OP. I believe that the main concern would be the loose coupling of > objects, IIUC you would need a Container method for each class *and* > Hum .. I don't understand here. What do you intend for 'a Container method for each class'? In my implementation there is only a Container class that is the central configuration point of the system. > each class would need to have an adapted initialize method, well that > Yes, and indeed this is very annoying. When the system grows up you have to do with long constructor signatures: Foo.new(logger, service, another_service ... ) But I wonder if there is a better method to keep things decoupled.. > just really seems too much work to me. > > Maybe I shall answer Robert's question at the same time ;) > > 41. A logging mixin > > module MyLogger > logger = Logger::new $stderr > define_method :log do logger end > end > > now all you have to do is to do include MyLogger in all your classes > and it is sufficient to change the MyLogger module once only, that of > course has not yet any advantage over using a global logger variable > or constant. > But you might have much more flexibility by generalizing the approach > above as follows > > module MyLogger > logger1 = ... > logger2 = ... > class << self; self end.module_eval do > define_meyhod :included do |into_module| > logger = > case into_module > when Type1Module > logger1 > when Type2Module > logger2 > else > Logger::new $stderr > end > into_module.module_eval do > define_method :log do logger end > end* > > > > That would give you pretty easy central control of logger capabilities > on one single point of your project in a more transparent way. > Actually DI makes him carry quite a have load, does it not? > > Cheers > Robert >