• 沒有找到結果。

Some Component-Oriented Design Guidelines

Chapter 2. A GENERIC COMPONENT MODEL

2.5 Some Component-Oriented Design Guidelines

The component model presented so far has already provided a concept framework where software designs can be evaluated. Below we present several basic design rules that are commonly used in our system design and implementation. Although these rules stem from fundamental software engineering principles, they are presented more from a component- and reuse-oriented perspective. Hence we coin the term design style to indicate that these rules reflect our “preferred” way of structuring a complex system and what a good design looks like. Different sets of rules are certainly possible with different design objectives under different situations.

Structure software designs in terms of the component model. The rule implies that given an artifact in a (composite) component, it should be straightforward to identify its role, i.e.

whether it is part of the domain, polymorphism, collaboration, or composition of the component. Each artifact should dedicate to one role. In particular, artifacts involved in composition should only concentrate on managerial tasks and create a smooth working environment for the components being composed; they do not involve in actual service providing in an essential way. Similarly, managed components should not be aware of and/or spend efforts on high-level managerial tasks. Such a worker-manager separation principle is in fact the foundation of our model.

Components should be designed with maximal applicability and usability. A component is more applicable than the other (with roughly the same functionality) if it can be used, conceptually, under more contexts then the other. A component is more usable if the effort needed to fit the component into a design is less. Usability also reflects the effort needed for

one to understand the component in order to make proper use of it. For example, a Java interface is considered more applicable than a Java class that implements it, but less usable.

We use reusability to summarize both driving forces that often contradict each other during design. Note that the reusability of a component also reflects the reusability of its parts. In other words, for two components with identical interfaces, the one with more reusable internals is considered more reusable. As a result, the tradeoffs between applicability and usability for each of the components in a larger design may become difficult to make.

A component should concentrate on single domain of service that covers exactly what it offers;

the component should know and use services from its context no more than what it needs. This rule is derived from the maximal reusability principle but from a more atomistic, component-oriented perspective. If the domain is incomplete for the problem a component intends to solve, the usability is hurt because it implies the missing part is substantial and not easy to fill in. If the domain supports multiple purposes, breaking it up into many single-purpose domains increases applicability.

One intention of the self-containedness principle is to achieve separation of concerns – one can inspect the component with minimal efforts spent on tracing its correlations with other components. A quick example is to design subroutines with strong cohesion and good names indicating their purposes exactly [6]. The modular design principle governing

module cohesion and coupling [5] is another example.

Increase component reusability by focusing on core functionality and delegating irrelevant decisions to context. For a valuable component (e.g. which contains code that has undergone substantial engineering effort), we can consider maximizing its reusability by introducing a dedicated interface that is geared toward its own domain, and declares the exact help it needs through polymorphism. An immediate example is the JTree component from the Java Swing library. Instead of depending on a specific definition of tree representation (i.e.

TreeNode) where the children addition and manipulation are somewhat irrelevant for displaying purpose, a dedicated interface TreeModel is created just enough for JTree to perform. This practice results in more applicable components, but incurs extra burden both from performance and usability perspective. In contrast to a compact design that is engineered for a specific problem, however, the principle may lead to extra wrapping layers, in exchange for overall reusability and adaptability.

Favor simple collaboration. When decomposing a component into smaller ones, it is desirable that the design results in simple collaboration among components. Complicated collaboration not only hurt the usability of the involving components, but also their applicability due to the constraints imposed. This is the reason people prefer not to use (deep) inheritance when object composition – a simpler form of collaboration – suffices.

The complexity of a collaboration relation is not determined just by which language mechanism is used, but rather by the efforts required to understand and use it. For example, collaboration via global variables is consider bad, if not worst, since the components need to be studied are potentially unlimited, and race conditions need to be analyzed. As a rule of thumb, we favor memoryless over stateful collaboration, where the latter implies that both parties need to maintain the state correctly. In addition, we favor one-way over bidirectional collaboration. One-way collaboration implies that one component can carry out the request processing without requesting additional information from its requestor.

Object composition without passing object references around (e.g. for callbacks) is considered one-way. As we regard inheritance a collaboration scheme between the base class and the subclass, deep inheritance may result in complicated collaboration that involves a chain of parent-child communication.

Chapter 3. A DESIGN ANNOTATION AND EVALUATION

相關文件