Showing posts with label Design. Show all posts
Showing posts with label Design. Show all posts

Thursday, July 21, 2011

Autonomous and Intelligent Object

I insist on that the object must be autonomous, that means it should own its data and behavior that can express its features. It will responsible for managing its data and state. The autonomous object reflects the basic principle of OO, that is "The data and the related behaviors should be encapsulated together." The object which owns the behaviors is smart, it is like it has a general sense, to judge for itself, without other objects informing. Other hand, the object should be an expert who can handle and manage its data.

For example, we need handle the parameters which are submitted by the customer to fetch the parameter objects we want to get in our project. The values of these parameters are placed in the web request. In advance we have get the information of parameter type according to the configuration. We provide three kinds of parameter:
1. Simple Parameter
2. Item Parameter
3. Table Parameter

I have defined the ParameterGraph class which can read the configuration info and create the concrete parameter subclass object by the parameter type. These parameter classes extends the same abstract class ParameterBase, and the ParameterBase implements the Parameter interface. The class diagram is as below:

parameter Because the data of parameters are stored in the web request, so we must parse the request. Essentially, the parameters in the web request are stored in the map, we should distinguish the parameters according to their name, then fill the data into the corresponding parameter object. We think it is the process of collecting the data of request and assembling parameter objects. This behavior is defined in the ReportParameterAction class. At first, I implement this function like this:

private Map<String,Parameter> collectParameters(ServletRequest request, ParameterGraph parameterGraph) {
for (Parameter para : parameterGraph.getParmaeters()) {
Map
<String,Parameter> paraMap = new HashTable<String, Parameter>();
if (para instanceOf SimpleParameter) {
String[] values
= request.getParameterValues(para.getName());
para.setValues(values);
paraMap.put(para.getName(),para);
}
else {
if (para instanceOf ItemParameter) {
ItemParameter itemPara
= (ItemParameter)para;
for (Item item : itemPara.getItems()) {
String[] values
= request.getParameterValues(item.getName());
item.setValues(values);
}
paraMap.put(itemPara.getName(),itemPara);
}
else {
TableParameter tablePara
= (TableParameter)para;
String[] rows
=
request.getParameterValues(para.getRowName());
String[] columns
=
request.getParameterValues(para.getColumnName());
String[] dataCells
=
request.getParameterValues(para.getDataCellName());

int columnSize = columns.size;
for (int i=0; i < rows.size;i++) {
for (int j=0;j < columns.size;j++) {
TableParameterElement element
= new TableParameterElement();
element.setRow(rows.get(i));
element.setColumn(columns.get(j);
element.setDataCell(dataCells[columnSize
* i + j]);
tablePara.addElement(element);
}
paraMap.put(tablePara.getName(),tablePara);
}
}
}
return parameterGraph.getParameters();
}

There are different logic to handle the different kind of parameter. For instance, if we want to collect the TableParameter, we must recoginize the row, column and data cells of table, and let their names to be key of map, and the value of map is the String array in fact. When I was writting these code like this, my intuition tell me that it exists bad smell. Maybe something is going wrong? In the collectParameters() method, there is very terrible branch statement. It will determine the runtime type of parameter to choose the differant processing logic. Why should we do like this? According to the class diagram, the parameter subclasses have the different method to process the value of paramter, such as getValue(), getItems(), getElement(). These methods can not be generized into the abstract super class. So that we must downcast the concrete type to invoke the corresponding method.


Now, let’s consider the issue from two perspective. First, who own the data which be processed by branch statement? These data are belong to the related parameter object except the request as the datasource for parameters. So, according to the OO principle described above, we should encapsulate these processing logic into the corresponding parameter subclass.


Second, we might consider about the abstraction of parameter. Although the different parameter has the different value, and the behaviors of process these value are different also, so we can’t generalize these logic. In fact, if you agree this opinion, it means you don’t really understand the meaning of abstract. Because we focused on the details, but not analyze the interface from the perspective of common features.  In the previous description, I have referred to this common feature, that is: whatever value you want to process, you want to fill these value into the parameter object essentially, as for how to fill, it belongs to the scope of implementation details. So we can define such method in the paramter interface:


public interface Parameter {
public void fillData(ParameterRequest request);
}

Note, I play a trick here. Comparing between the fillData() and collectParameters() methods, they accept the different argument. In fillData() method, I provide the ParameterRequest to replace the ServletRequest. What happen?


Two reasons. First, the ServletRequest interface is defined in the Servlet. If Parameter object want to use ServletRequest, it must depends on the Servlet package(Parameter and ReportParameterAction belong to two different packages, we can’t involve the dependency in the package that Parameter belong to). Second, ServletRequest interface is a huge interface, and it is difficult to implement. Consider about this situation: we want to do unit test for Parameter, it is very difficult to mock ServletRequest interface. In fact, we just want to invoke the getParameterValues() operation here. So, I provide the ParameterRequest interface:



public interface ParameterRequest {
public String[] getParameterValues(String name);
}
Let’s continue to look at the processing of parameter. We move the processing logic into the fillData() method of each parameter subclass:
public class SimpleParameter extends ParameterBase {
public void fillData(ParameterRequest request) {
this.setValues(request.getParameterValues(this.getName()));
}
}
public class ItemParameter extends ParameterBase {
public void fillData(ParameterRequest request) {
for (Item item : this.getItems()) {
String[] values
request.getParameterValues(item.getName());
item.setValues(values);
}
}
}
public class TableParameter extends ParameterBase {
public void fillData(ParameterRequest request) {
String[] rows
=
request.getParameterValues(
this.getRowName());
String[] columns
=
request.getParameterValues(
this.getColumnName());
String[] dataCells
=
request.getParameterValues(
this.getDataCellName());

int columnSize = columns.size;
for (int i=0; i < rows.size;i++) {
for (int j=0;j < columns.size;j++) {
TableParameterElement element
=
new TableParameterElement();
element.setRow(rows.get(i));
element.setColumn(columns.get(j);
element.setDataCell(dataCells[columnSize
* i + j]);
this.addElement(element);
}
}
}
}

Now, these parameter objects all are autonomous objects, they can handle their data, don’t need the other object tell them. They have the ability of judge there behavior intelligently. Let’s look at the code snippet of client:


private Map<String,Parameter> collectParameters(ServletRequest request, ParameterGraph parameterGraph) {
for (Parameter para : parameterGraph.getParmaeters()) {
Map
<String,Parameter> paraMap = new HashTable<String, Parameter>();
para.fillData(
new ParameterRequest() {
public String[] getParameterValues(String name) {
return request.getParameterValues(name);
}
);
}
return parameterGraph.getParameters();
}

Because the parameter itself can process its data from the request, so ReportParameterAction doesn’t consider about these complex situations. The structure of the code becomes more clear, and the responsibilities of these objects becomes more accurate. It improves the extensibility of the program due to we do the correct encapsulation to remove the branch statement. That is a benifit of autonomous object. Please keep in your mind that autonomous principle and expert pattern while you want to assign the responsibilities.

Tuesday, June 21, 2011

Design decides on performance

In our legacy system, we found a stupid design decision. We provide the service of business suite for our customer.  The relationship between customer and business suite is stored in the user_busi_suite table. When the administrator of system want to delete the specific business suite, the program should check whether the business suite was subscribed by user at first.  If the business suite had been subscribed, it will popup the alert window to give the noticement.
Guess how to implement this function in the legacy system? It fetched the all user who subscribed this suite, then draw a dicision whether delete it depending on the size of user list:
List users = busiSuiteRepository.fetchSubsribersWith(suiteId);
if (users == null || users.size() == 0) {
    busiSuiteRepository.remove(suiteId);
}
Its implementation will effect on the performance seriously. The query of the user with suite is not necessary. We should better define the property for BusinessSuite which indicates whether it was subscribed by user. Like this:
public class BusinessSuite {
    private boolean subscriptionFlag = false;
    public boolean isSubscribed() {
        return subscriptionFlag;
    }
    public void subscribe() {
        subscriptionFlag = true;
    }
}

BusinessSuite bs = busiSuiteRepository.with(suiteId);
if (!bs.isSubscribed()) {
    busiSuiteRepository.remove(suiteId);
}
Don't need to query the list of user, we only get the value of subsription flag to draw a decision. That's easy!

Tuesday, July 13, 2010

Art of Object Design on MPDay

Last sunday, I gave a speaking as a speaker in the msup open day conference in ShangHai. My topic was Art of Object Design, mainly focused on low level design in the software development lifecycle. I want to dig the essence of software design deeply. As we know, there are amount of principles and patterns for software achitecting and design. It is impossible to grasp all of these. You know more, understand less. Through by analyzing the essence and core idea of design patterns and architecture patterns, I summarized seven principles including Reusability, Extensibility, Separation, Change, Simplicity, Consistency, Indirection.

   

Reusability

The most evil enemy in the software development is repeat. It results in developing repeaded so that we cann't reuse some components and functions effeciency. It would generate the bad smell of solution sprawl. How to avoid repeat? We must keep the objects to be fine-grained and high cohesion. It needs to use encapsulation reasonable.  We can improve the reusability of software based on three-level: method, class and module. For instance, we can extract method or super class, define some helper class, devide modules by dependency.  The following diagram demonstrates how to apply template method pattern to reuse some codes in JUnit Framework:

image

Extensibility

The excellence structure of software should be extensible so that we can add functions without modifying the source code. There are two meaning of extensibility. First we don't add new features to expose it, that is an extensiblity internal.  We can decorate the target object or control it by providing a proxy object. Second is an extensiblity external. The inheritance and composition are the common approach. Of course, we don't forget the abstraction. For example, the Runnable interface in Java supports the extensiblity of business when you want to write the multi-thread program.

class MyThreadStart implements Runnable {
     public void run()  {
        //do something
    }
}

Thread controller = new Thread(new ThreadStart());
controller.start();

Separation

Concern separation is the most important principle for architecting architecture. The classic patterns which embody the concern separtion are layered architecture pattern and MVC pattern. The key element of separtion is that seperating changeful resposiblity from changeless object. That is core value of SRP also. Of course, we must focus on the collaboration between objects too. The following diagrams list my opinon about separation:  imageimage image

Change

One of the inevitable thing is change in the software development. So we must find the change point when we analyze the requirement. In my experience, I think these points always are changed as below:

1. Business Rule (Solution: Specification Pattern)

2. Algorithm and Strategy (Solution: Strategy Pattern)

3. Command and Request (Solution: Command Pattern)

4. Hardware Environment (Solution: Abstraction Layer or Gateway Pattern)

5. Protocol and Standard (Solution: Metadata)

6. Data Schema (Solutioin: Encapsulate Data or Information Hide)

7. Business Flow (Solution: Customize Workflow)

8. System Configuration (Solution: Metadata or Database)

9. User Interface and Presentation (Solution: Layered Pattern, MVC Pattern)

10. External Services (Solution: Abstraction Layer or Service Facade)

Simplicity

There are two important principles we have always to keep in mind. These are KISS(Keep it simple and stupid) and YAGNI(You aren't gonna need it). How to simplify the complex implemention? We can use encapsulation to hide the complex implementation. The abstraction is the other way. It might unify model, and elimilate the difference. As architect, most of them want to pursue the perfect solution. It's wrong! Many anti-patterns related with this idea, such as Analysis Paralysis, Accidental Complexity.

Consistency

So called "Consistency" includes interface, format, invoking way and solution. If we have consistant interface, so the implementation can be substituted. If we have consistant format, we can infer the overall design from the part of design. Consistant invoking way would help client understand the intention of service provider. Consistant solution is the basic of cooperation of team. For example, we can achieve the consistant invoking way by using composit pattern:

image Indirection

David Wheeler said:"All problems in computer science can be solved by another level of indirection". Indirection in software programming is achieved by delegation, abstraction and collaboration. Indirection can reduce dependence, hide the detail and simplify the client call. Many pattens are reflect the indirection thought, such as Facade Pattern, Mediator Pattern, Adapter Pattern, Strategy Pattern, Service Locator Pattern.

Wednesday, April 15, 2009

Design is an island

Recently, Kent Beck wrote a post which raise an opinion about software disign. It's a nice metaphore for disign. That is: design is an island.

Kent wrote: Designing, then, is like walking an island. As long as you don't get your feet wet, your design is okay. It means that we should try to design enough to meet the current set of requirement, otherwise you might fall into the water.

The "water line" is always changed because the tides always change the sea level. It means the change of requirement. That is inevitable. So we must beware not to let your feet wet. Kent said:"Design that are acceptable at other time of the year break down in the 10% of the year when you do 50% of your business." So you should keep your design fresh. It's the feature of the excellent architecture.

"Climbing higher on irland requires effort, just as improving designs require effort." That's right. You should spend your money and time to improve your disign.

Kent recoginzed the distributed application such as SOA and REST as the archipelago. It's interesting. The analogy is perfect. It's the extension of metaphore of island.

Kent alos raise his opinions, for example earthquake and island sank. More details visit the kent beck's blog here. Enjoy it!