Freitag, 17. Juni 2011

Let it flow...

The Object Modeling Technique (OMT) introduced by James Rumbaugh defines for the analysis phase (OOA) three modeling types. Starting at the object level, defining an object model, the dynamic modeling is following for the core parts of the business logic mainly to visualize state changes. Object models and dynamic models covered through state diagrams are present in the UML today. The functional modeling, also known as data flow models, are not part of the UML. The reason might be the functional background of data flows, not recognized as an important modeling technique in the object-oriented world.

For my computer science degree I designed with OMT and developed a multimedia system in the programming language C which is a portable solution for Microsoft Windows NT and IBM OS/2. I designed  in the object-oriented style and used the functional programming language C to implement the model, that was possible with some tricks and effort. I remember one important point during modeling the multimedia system. I had some problems with the object model relationships and sometimes problems to define the dynamic model in detail. The data flow modeling was easier for me, more natural and rather fluent. 

Data Flow Diagram
At that time I thought the problem is my weak experience in modeling complex object-oriented systems. Later in my career I learned object-oriented modeling starting with the domain layer (DDD). I learned to visualize the software model in class and component diagrams using stereotypes and the power of UML diagrams. This power is in some cases weak when it touches the implementation and the differences in the model and the implementation occur. Continuous rework for the improvement of the models are necessary to avoid this weakness. Modeling on an appropriate level with appropriate details is not simple and some lessons are necessary to learn modeling on the right level. My advice is to try the Java EE architect certification as an opportunity to find this level.

During last weeks, I followed the discussion about Event Based Components (EBC) which is a technique to develop software flow-oriented. Ralf Westphal defined principles and patterns for the flow-oriented programming. I read some of his blog articles and listened to the discussions in the clean code developer forum. It seems to me that the .NET community is very eager in the field of flow-oriented programming. Later, I found a website from a developer whose aim is to bring EBC to the Java platform. Impressed of the flow-oriented modeling which again seems natural and fluent, usable on different levels and in line with the implementation, I was inspired to find a Flow Pattern

Therefore it was obvious to start Eclipse to do some TDD practice to dive into the flow. I admit that my knowledge about flow-oriented programming is weak and I rather think in object-oriented structures. This is what I have learned over the years developing software on the Java platform. Usually it is a worse starting position developing software in a field with weak domain knowledge, but it’s still a training  session. Starting with this background programming flows leading to a solution for a simple pattern for flow-oriented programming. Do not expect the "killer pattern" for a wide range of scenarios and problem solutions covered by this flow pattern.

The pattern is following the principle of the “input-process-output” model. It is usable in isolation or in chained scenarios. The use case for the pattern is a simple calculator board adding and subtracting values encapsulated in assemblies. The following discusses the flow pattern that is first documented with UML.

Flow Pattern

The “FlowChain” is an inspiration of the ChainOfResponsibility pattern. The ChainOfResponsibility pattern loosely couples assemblies and let the assemblies decide whether to process a value or not. The core of the pattern is based on Template Method by overriding the flow method to chain assemblies. The benefit of this pattern is to chain in the overriden flow method which is well encapsulated. The drawback is that you can only test assemblies covering their relationships, chained in the flow method. It’s not possible to test assemblies in isolation, that might be a drawback for some use cases.

The interface of the flow pattern is simple by calling the process method of an assembly instance which extends the flow pattern. The process method expects an input pin and returns an output pin as the result of the processing task. It’s not possible to build the chaining outside of the assemblies which is flexible but on the other side a slightly more complex programming interface.

The “FlowWire” is weaker coupled than the “FlowChain” with the possibility for outside wiring and testing of assemblies in isolation. In the flow pattern implementation the "FlowWire" extends the “FlowChain” with a wire method for outside wiring. The pattern consists of small super classes as the base for boards or assemblies of the boards using pins to fulfill the requirements of the “input-process-output” model.

The flow assemblies are based on classes whose instances are chained. It’s also possible to see flows as a chain of method calls which is finally more fine grained. The flow pattern covers simple use cases, not supporting complex routings and broadcasts.

FlowChain (stronger coupling)

public abstract class FlowChain<T> {
               
    public FlowPin<T> process(final FlowPin<T> inPin) {

        return flow(inPin);
    }
   
    protected abstract FlowPin<T> flow(final FlowPin<T> inPin);
}

 FlowWire (weaker coupling)

public abstract class FlowWire<T> extends FlowChain<T> {
   
    public FlowPin<T> wire(final FlowWire<T> assembly,
                                      final FlowPin<T> inPin) {
               
        return assembly.process(inPin);
    }
}

FlowPin

public class FlowPin<T> {

    private final T value;
   
    public FlowPin() {
       
        this(null);
    }
   
    public FlowPin(final T value) {
       
        this.value = value;
    }
   
    public T value() {
       
        return value;
    }

    @Override
    public int hashCode() {

        final int prime = 31;
        int result = 1;
        result = prime * result + ((value == null) ? 0 : value.hashCode());
       
        return result;
    }

    @Override
    public boolean equals(Object obj) {
       
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;              
        final FlowPin<T> other = (FlowPin<T>) obj;
        if (value == null) {
            if (other.value != null)
                return false;
        } else if (!value.equals(other.value))
            return false;
       
        return true;
    }
}

FlowChainTest

import static org.junit.Assert.assertEquals;
import org.ccd.flow.pattern.api.FlowChain;
import org.ccd.flow.pattern.api.FlowPin;
import org.junit.Test;

public class FlowChainTest {

    @Test
    public void testSubtractAssembly() {
      
        final FlowChain<Integer> subtractAssembly = new SubtractAssembly();      
        assertEquals(new FlowPin<Integer>(7), subtractAssembly.process(new FlowPin<Integer>(12)));
    }
  
    @Test
    public void testAddAssembly() {
      
        final FlowChain<Integer> addAssembly = new AddAssembly();      
        assertEquals(new FlowPin<Integer>(17), addAssembly.process(new FlowPin<Integer>(12)));
    }
  
    @Test
    public void testCalculatorBoard() {
      
        final FlowChain<Integer> calculatorBoard = new CalculatorBoard();      
        assertEquals(new FlowPin<Integer>(7), calculatorBoard.process(new FlowPin<Integer>(2)));
    }
  
    private class CalculatorBoard extends FlowChain<Integer> {

        @Override
        protected FlowPin<Integer> flow(final FlowPin<Integer> inPin) {
              
            final AddAssembly add = new AddAssembly();
          
            return add.process(inPin);
        }
    }
  
    private class AddAssembly extends FlowChain<Integer> {

        @Override
        protected FlowPin<Integer> flow(final FlowPin<Integer> inPin) {
              
            final Integer value = addValue(inPin.value());
            final SubtractAssembly subtract = new SubtractAssembly();                      
                      
            return subtract.process(new FlowPin<Integer>(value));
        }

        private Integer addValue(final Integer value) {
          
            return value + 10;
        }
    }
  
    private class SubtractAssembly extends FlowChain<Integer> {

        @Override
        protected FlowPin<Integer> flow(final FlowPin<Integer> inPin) {
              
            Integer value = substractValue(inPin.value());          
              
            return new FlowPin<Integer>(value);
        }
      
        private Integer substractValue(final Integer value) {
                              
            return value - 5;
        }
    }
}

FlowWireTest

import static org.junit.Assert.assertEquals;
import org.ccd.flow.pattern.api.FlowPin;
import org.ccd.flow.pattern.api.FlowWire;
import org.junit.Test;

public class FlowWireTest {

    @Test
    public void testSubtractAssembly() {
      
        final FlowWire<Integer> subtractAssembly = new SubtractAssembly();      
        assertEquals(new FlowPin<Integer>(7), subtractAssembly.process(new FlowPin<Integer>(12)));
    }
  
    @Test
    public void testAddAssembly() {
      
        final FlowWire<Integer> addAssembly = new AddAssembly();      
        assertEquals(new FlowPin<Integer>(22), addAssembly.process(new FlowPin<Integer>(12)));
    }
  
    @Test
    public void testCalculatorBoard() {
      
        final FlowWire<Integer> calculatorBoard = new CalculatorBoard();      
        assertEquals(new FlowPin<Integer>(7), calculatorBoard.process(new FlowPin<Integer>(2)));
    }
  
    private class CalculatorBoard extends FlowWire<Integer> {

        @Override
        protected FlowPin<Integer> flow(final FlowPin<Integer> inPin) {
          
            final FlowWire<Integer> add = new AddAssembly();  
            final FlowPin<Integer> outPin = wire(add,inPin);
          
            final FlowWire<Integer> subtract = new SubtractAssembly();          
            return add.wire(subtract,outPin);
        }
    }
  
    private class AddAssembly extends FlowWire<Integer> {

        @Override
        protected FlowPin<Integer> flow(final FlowPin<Integer> inPin) {
              
            final Integer value = addValue(inPin.value());
                                                  
            return new FlowPin<Integer>(value);
        }

        private Integer addValue(final Integer value) {

            return value + 10;
        }
    }
  
    private class SubtractAssembly extends FlowWire<Integer> {

        @Override
        protected FlowPin<Integer> flow(final FlowPin<Integer> inPin) {
              
            final Integer value = substractValue(inPin.value());          
              
            return new FlowPin<Integer>(value);
        }
      
        private Integer substractValue(final Integer value) {

            return value - 5;
        }
    }
}

The flow pattern is developed in an object-oriented manner to support the programming of flows. Their is usually a border between object-oriented and flow-oriented programming. But listen: "This border is in your mind". You see it's possible to mix different styles to find a solution. The Flow Pattern is object-oriented and the implemented flows in the test cases are a mixture of the flow-oriented and object-oriented style.Using the power of both styles is appropriate to define a solution that fullfils the criteria of "input-process-output", encapsulation, isolation, loose coupling (coupling happens only on the board) and high cohesion to develop small assemblies and wire them to build a bigger assembly named as board. Wire boards and build a bigger solution that I call the flow-oriented application.You see, let it flow...to cross the border.


Der Rechtshinweis des Java Blog für Clean Code Developer ist bei der Verwendung und Weiterentwicklung des Quellcodes des Blogeintrages zu beachten.