Refining Uncle Bob’s Clean Code(五)

来源:互联网 发布:linux 一键安装 编辑:程序博客网 时间:2024/06/07 05:40

Symmetry of abstraction

If you look at the original code (like method parseArgument() and some of its called methods) you might detect a mix of levels of abstraction. For example, deciding if the current argument represents the introduction of a new argumentId (by comparing the first character of the current argument with ‘-’) might be a level below the next command – parsing the element. I’m not sure about that, but i think encapsulating this decision within a method hides the concrete implementation, expressing only the purpose of the inspection of the current argument.

Open Closed Principle

What do we have to do, if we want to add a new Marshaler? As Uncle Bob mentioned in his book, you have to adapt more than one place in the code to integrate a new one. Essentialy you have to alter method parseSchemaElement(). Personally, i think this method lies on a to detailed implementation level for being a good place to add new Marshalers. You have to come a long path until you end up in this method.

I think, adding a new Marshaler should be done on the very top level of the ‘application’, in a very simple way: registering a new Marshaler and that’s it – no hassling with implementation details.
Again, if we look at class Args as the root of its ‘application’, that’s the perfect place for constructing the system, and hence registering all available ArgumentMarshalers. Since Marshalers.Factory is under control of Args, it can be ‘dependency injected’ simply by registering all available ArgumentMarshalers.

01public class Args{
02 
03    static{
04        Marshalers.Factory.register( new BooleanArgumentMarshaler() );
05        Marshalers.Factory.register( new IntegerArgumentMarshaler() );
06        Marshalers.Factory.register( new StringArgumentMarshaler() );
07        Marshalers.Factory.register( new DateArgumentMarshaler() );
08    }
09        ...
10}

I’m not sure if we can speak about violating OCP in the orig code, but now for me it feels more like adding new Behaviour by changing configuration, not by changing implementation.

Another point in relation to OCP is the addition of a new getter method that comes with every new ArgumentMarshaler (eg. getDate() forDateArgumentMarshaler). If you want to avoid that, you could of course come up with a more generic method, using the type token idiom (that may be not type safe) – so let’s complete Args:

01public class Args {
02        ...
03    public <T> T get( String argumentId, Class<T> asType ) throws ArgsException{
04        return (T) argumentMarshalers.getMarshalerFor( argumentId ).get();
05    }
06 
07    public int cardinality() {
08        return argumentMarshalers.size();
09    }    
10 
11    public boolean has( String argumentId ) {
12        return argumentMarshallers.isMarshalerAvailabeFor( argumentId );
13    }
14 
15}

Of course, we could have made the get() method more type safe by doing a type check with a following cast and maybe providing a default value of for the claimed type if the client didn’t ‘hit’ the appropriate type for a certain argumentId. But at the end of the day, the client should handle its mistake, so i think it’s better to come up with an exception.

Extinguishing the campfire

As you have seen, there are quite a few changes to Uncle Bob’s orig code. I think at the end it’s the surrounding forces and given values that will drive the final structure of the code.

Of course, the refactored solution has still some obstacles and cornercases – now it’s up to the next boy scout to move on … ;o)

For me, those changes reflected the way i think and try to communicate my intentions through the code. That may not be the way of communication for other developers. In general, it’s worth to take a deeper look into the psychological context of software development when it comes to communication. Moving forward to a common understanding of how to transport intention and meaning between humans might be a key to cleaner code in respect to its readability.