hengheng123456789

  BlogJava :: 首页 :: 联系 :: 聚合  :: 管理
  297 Posts :: 68 Stories :: 144 Comments :: 0 Trackbacks

POAD Beginning

 

Design patterns are immensely powerful, but to build large-scale robust systems, you need more. Pattern-Oriented Analysis and Design introduces a methodology for "composing" proven design patterns into reliable, robust large-scale software systems. Using POAD, you can quickly build systems that are far more robust, scalable, and maintainable-using UML class diagrams as your building blocks.

Pattern-Oriented Analysis and Design takes design patterns to the next level. Whether you're an architect, designer, developer, or manager, it will help you build better software systems faster.

 

 

EXPFeedback Control Systems

 

Control systems are widely popular in real-world applications. They are often implemented in many software applications, whether to control an external environment or to control other software components in a system. In this chapter we use the POAD process and models to develop a reusable pattern-oriented design framework for feedback control systems.

Design patterns have been deployed in the design of many domain-specific applications that have feedback properties. For instance, patterns are used to develop the manufacturing system discussed by Schmid in [Schmid 1995]. The development of such a framework starts by using traditional OO constructs such as classes and objects. Design patterns are then used to develop a generic and flexible framework by refactoring the existing design. Through the use of patterns, the design framework becomes generic enough to use in many automated manufacturing systems like assembly lines. Another experience of using patterns in developing applications for manufacturing systems is illustrated by Bosch in "An Object-Oriented Framework for Measurement Systems" (1998a). These experiences and others are practical examples of using patterns as fundamental elements in the design of software applications for manufacturing systems.

Feedback systems are commonly modeled using block diagrams. The design framework that we develop in this chapter is based on design patterns as building constructs. The framework is documented at various design levels using POAD models and is reusable as an initial phase in designing feedback control applications. To develop a pattern-oriented design for feedback control systems, we follow the POAD process outlines, as discussed in Chapter 7, and the detailed process described in chapters 8, 9, and 10.

Recall the three development phases outlined in Chapter 7: analysis, design, and design refinement. Each phase contains activities (steps), as discussed in chapters 8, 9, and 10. In the following sections we discuss the application of the POAD activities to develop the framework for feedback control systems. There is no absolute need to follow a pure waterfall model; the designer initially follows these activities and can always iterate on them as long as the models developed from each step are documented and traceable to the outcomes of other activities.

POAD Analysis for the Feedback Control Framework

Requirements Analysis

A closed-loop control system can be decomposed into components based on independent responsibilities. To implement a feedback control system, the specification and description of the system configuration and its components must be put into a form amenable to analysis and design. Three basic representations (models) of components and systems are used extensively in the study of control systems: mathematical models, block diagrams, and signal-flow graphs. Referring to control literature [e.g., Distefano et al. 1990], the generic block diagram of feedback systems represents an initial architecture documentation to start with. A feedback control system is one in which the control action is dependent on the output. Feedback is the property of a closed-loop system that permits a measured output to be compared to the system's input so that the appropriate control action may be performed as some function of the output and the input. Figure 11-1 illustrates the block diagram that is often used to describe a feedback control system.

Figure 11-1. Block diagram for a feedback control system.

Many practical reactive systems encompass software control applications in their implementation. The portion of a system to be controlled is usually called the plant. A plant is to be accurately controlled through feedback operation. An output variable is adjusted as required by the error signal. The error signal is the difference between the system response as measured by the feedback element and the reference signal, which represents the desired system response. Generally, a controller is required to process the error signal such that a certain control strategy will be applied. We are not concerned here with the theoretical analysis techniques of a feedback system; instead, we concentrate on the pattern-oriented design of the feedback system in a reusable form that is easy to implement and use.

To analyze a feedback system, we may use a UML use case diagram. Figure 11-2 illustrates a use case diagram for the feedback system. In this diagram, we identified three actors interacting with the system: the user who configures the system and provides the input data for adjusting the plant, the sensors that measure some data from the plant, and the actuators that control the plant. We have also identified several use cases: the configure use case in which the user provides the input parameters used in controlling the plant; the monitor use case in which measurement data is collected; the control use case in which the plant is controlled by some actuators; the calculate error use case, which determines the difference between the measurements taken from the plant and the required values set by the user; and the collect data use case, which collects all statistics and measurements taken during the operation.

Figure 11-2. A use case diagram for the feedback system.

Further analysis can proceed at this level by starting the development of interaction diagrams for the identified use case. Interaction diagrams help in defining the components and their interactions, and hence we are able to determine the logical components and their functionalities. However, for this case study, the use cases are simple, and it is easy to determine a set of logical components.

Using the generic block diagram of a closed-loop control system and the use case diagrams, the system is decomposed into the following components:

·         A feedforward component, which handles the error data and applies a control algorithm to the plant.

·         A feedback component, which takes measurement data from the plant, processes it, and provides the feedback data.

·         An error calculation component, which compares the input and feedback data and produces the error.

·         The plant, which is an external component controlled by the system.

At the end of this activity, the system is decomposed into a set of components, and the functionalities and responsibilities of each component are identified. The description provided above is succinct but sufficient to proceed to the next development activities. Now we proceed to the process of selecting suitable patterns to implement the functionalities of these conceptual components.

Pattern Selection

We analyze the responsibilities and the functionalities of each component and identify candidate patterns that could provide a design solution for each component. In this step, we consider the design problem that we want to solve and match it to the solution provided by general-purpose design patterns, as those documented in [Gamma et al. 1995; Buschmann et al. 1996; PLoPD, PLoPD2, PLoPD3, PLoPD4]:

1.    The feedforward component implements a control strategy. The design should provide flexibility in the selection and deployment of various control strategies with minimal impact on other components in the system. For example, the feedforward component should provide the same interface to the rest of the components in the system, while the framework can provide the flexibility to plug in and take out different control strategies. If we consider this as the design problem that we want to solve and search for patterns whose intent is to solve similar problems, we find that a Strategy pattern [Gamma et al. 1995, pp. 315] is a good candidate for this task. Recall the intent section of the Strategy pattern: "Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it." Hence, the Strategy pattern solves the design problem of the feedforward component.

2.    The feedback component receives measurements and applies a feedback control strategy. It feeds the results from comparing (according to a specific strategy) the feedback data with the input data to the error calculation component. The measurement unit observes and measures data from the plant and feeds it to the feedback branch. A mechanism is required to deliver measurements to a feedback controller. Measurements can be delivered to the feedback controller using the Observer pattern [Gamma et al. 1995, pp. 293]. Recall the intent section of the Observer pattern: "Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically." Thus we can use the Observer pattern to loosen the dependency between the objects doing the plant observation and those actually doing the feedback control operation. Measurement data is fed to the feedback control strategy, which—similar to the feedforward component—applies a specific control strategy. The design should provide the capability to plug in and take out different feedback control strategies. This can be implemented using another Strategy pattern.

3.    In the error calculation component, the feedback controller notifies the error calculation unit with the feedback data. The feedback controller can be viewed as the subject that notifies the error calculator with changes in the feedback data. Error calculation is done at the moment feedback data becomes available; this data is compared with the input data. Thus, an Observer pattern can implement this behavior.

4.    If we examine various data manipulated in the feedback system, we find that the system manipulates measurement data that is measured from the plant; feedback data that is the result of processing the measured data by the feedback element; and error data that is the result of processing the feedback data and the input data. Data of different types need to be exchanged between the framework components. We can use a Blackboard pattern (a modified version of the blackboard patterns in [Rogers 1997; Buschmann et al. 1996]) for managing the system repository.

To summarize, in this step a set of patterns is selected to fulfill the responsibilities identified for each conceptual component—that is, those that we identify in the requirement analysis activity. In choosing these patterns, we consider how the pattern solves the design problem and the intent of the pattern. A Strategy pattern is selected for the feedforward component, an Observer and a Strategy pattern are selected for the feedback component, an Observer pattern is selected for the error calculation component, and a Blackboard pattern is selected as the system repository. In this simple example, it is obvious which patterns can be used. In other complex examples, the analyst could use UML use cases and sequence diagrams to understand the functionality required by each component.

POAD Design for the Feedback Control Framework

Constructing Pattern-Level Diagrams

In this step we create instances of the selected patterns and identify the relationships between these instances. As a result, a Pattern-Level diagram of the system is developed.

First, we create pattern instances. Recall that in this step we just give domain-specific names to abstract patterns types. In the previous step, we have chosen two Strategy patterns: one in the feedforward component and the other in the feedback component. Thus, we use the instances FeedforwardStrategy and FeedbackStrategy of type Strategy pattern in the design of the feedforward and feedback components respectively. We have also selected two Observer patterns: one for the feedback component and the other for the error calculation component. Thus, we use a FeedbackObserver instance of type Observer pattern to observe and measure data from the plant and an ErrorObserver instance of type Observer pattern to calculate the error. We use a Blackboard instance of type Blackboard pattern to manage the system data repository.

Second, we define dependency relationships between pattern instances. The FeedbackObserver uses the FeedbackStrategy to apply a feedback control algorithm, which in turn uses the ErrorObserver to calculate the error. The ErrorObserver uses the FeedforwardStrategy to apply a forward control algorithm. The Blackboard is used by all patterns to store and retrieve data.

Finally, we use the pattern instances and their relationships to construct the Pattern-Level diagram, as shown in Figure 11-3. We use UML stereotypes to show the type of the pattern instance.

Figure 11-3. A Pattern-Level diagram for feedback control systems.

The product of this step is the Pattern-Level diagram of the framework. It describes the architecture of a feedback system using design patterns, which explains why the names Pattern-Oriented Analysis and Design and Pattern Oriented Framework are used. This diagram could be revisited at a later phase to iterate on the decisions that we made (as part of an iterative development lifecycle). For instance, during the design or design-refinement phases, we might discover that a selected pattern has limitations or impacts on other design aspects. In this case the designer revisits the Pattern-Level design diagram to choose another pattern and replaces previous choices or creates a new pattern dependency or a new uses relationship.

Constructing the Pattern-Level with Interfaces Diagram

In this step, we analyze the relationships between pattern instances. The dependency relationship between patterns in the pattern-level view is a conceptual, high-level dependency relationship that should be further traced to lower-level design relationships between pattern interfaces.

First, we declare interfaces for the patterns used in each Pattern-Level diagram (only one diagram for the feedback system). Interfaces for most of the patterns used in our case studies are defined in Appendix A. The Strategy pattern has the class Context as the interface to the encapsulated control strategy. The Observer has two interfaces that allow coordinating the subject observed with its observer. These interfaces are the notify() interface operation in the subject and the update() interface operation in the observer. The Blackboard pattern has the interfaces to get and store data in the repository; these interfaces are the getData() and setData() interface operations. Note that at this level do not consider any details related to the parameters used and their types.

We then identify the relationship between pattern interfaces by translating all dependency relationships between patterns in a Pattern-Level diagram to relationships between interface classes and/or interface operations. The product of this process is the Pattern-Level with Interfaces diagram. Figure 11-4 illustrates the Pattern-Level with Interface diagram for the feedback control framework.

Figure 11-4. A Pattern-Level with Interfaces diagram for feedback control systems.

Let's take an example: the relationship between the FeedbackObserver and the FeedbackStrategy pattern instances in the Pattern-Level view. The relationship between these two patterns is that the FeedbackObserver uses the FeedbackStrategy to apply a feedback control strategy whenever the measurement data is ready. The interfaces of the FeedbackObserver are the Update() and the notify() interface operations. The interface of the FeedbackStrategy is the Context interface class. Thus, the relationship between these two patterns is translated to a relationship between the Update() interface operation of the former and the Context interface class of the latter. Similarly, all pattern relationships of Figure 11-3 are translated to relationships between the pattern interfaces in Figure 11-4.

Constructing Detailed Pattern-Level Diagrams

To construct the Detailed Pattern-Level diagram, we express the internals (i.e., participants) of each instantiated pattern in the Pattern-Level with Interfaces diagram. Since we have used pervasive design patterns in developing the feedback control framework, their structure can be found in the literature. For example, the class diagram model for the Strategy and Observer patterns is documented in the GoF book (1995). The diagram in Figure 11-5 shows the Detailed Pattern-Level diagram for the feedback pattern-oriented framework.

Figure 11-5. A Detailed Pattern-Level diagram for feedback control systems.

Note that we do not make any additional design decisions in this step. With the appropriate tool support, Figure 11-5 is a direct generation from the Pattern-Level with Interfaces diagram by simply retrieving the internal class diagram model for each pattern from a pattern database.

POAD Design Refinement for the Feedback Control Framework

Instantiating Pattern Internals

In this step we add domain-specific nature to the Detailed Pattern-Level diagrams by renaming internal pattern classes according to the application domain, choosing names for pattern participants that are meaningful in the application context, and defining domain-specific names for operations in the patterns. In the following, we instantiate the pattern internals for each pattern that appears in Figure 11-5.

The FeedforwardStrategy pattern (Figure 11-6) is composed of

·         Controller. The context of the control strategy that is configured with a concrete control strategy object through a reference to an AbstractController interface.

·         AbstractController. The interface for all concrete control strategies. The Controller uses this interface to invoke the concrete control strategy algorithm through polymorphism invocations.

·         ControlStrategyA and ControlStrategyB. These are concrete control strategies that represent various implementations of the control strategies that the designer can choose from.

Figure 11-6. Instantiating the FeedforwardStrategy pattern.

The error calculation component consists of the ErrorObserver pattern (Figure 11-7), which is composed of

·         AbstractObserver. An updating interface for objects that are notified of changes in the subject.

·         AbstractSubject. An interface for attaching and detaching observers. It knows about its observers that ought to be notified of a subject's change.

·         ErrorObserver. It is a concrete observer that maintains a reference to the FeedbackSubject, reads the feedback data after being processed by the feedback strategy, analyzes the feedback data with respect to the reference input data, and stores the error in the blackboard. It implements the AbstractObserver updating interface.

·         FeedbackSubject. It is a concrete subject that sends notification to the concrete observers of new data received from the feedback component.

Figure 11-7. Instantiating the ErrorObserver pattern.

The FeedbackObserver (Figure 11-8) is used in the feedback component and is composed of

·         AbstractObserver and AbstractSubject. They play an interface role similar to that of the ErrorObserver pattern.

·         MeasurementSubject. It receives measurement notifications from the plant and notifies its observer, FeedbackObserver, that a measurement is ready.

·         FeedbackObserver. When notified by changes in the plant (through the MeasurementSubject), it pulls the data identifier from the subject (using the pull mode of the Observer pattern) and invokes the feedback controller to process the measured data.

Figure 11-8. Instantiating the FeedbackObserver pattern.

The Blackboard (Figure 11-9) is composed of

·         Blackboard. It is the interface for retrieving and storing data. For the error component, it stores error data for successive readings and provides interfaces for retrieving and storing data records.

·         DataHolder. It is an interface to all types of data. This class is added to facilitate manipulating and referring to data in various class method signatures.

·         ErrorData. The concrete error data record to be stored in the application repository.

·         MeasuredData. The concrete data record measured from the plant.

·         FeedbackData. The data after being processed by the feedback control strategy.

Figure 11-9. Instantiating the Blackboard pattern.

The FeedbackStrategy (Figure 11-10) pattern is composed of

·         Feedback. It is the context of the feedback control strategy. It is configured with a feedback control strategy object through a reference to an FBAbstractController.

·         FBAbstractController: It is the interface for all feedback control strategies. The Feedback uses this interface to call the feedback concrete algorithm.

·         FBControlStrategyA and FBControlStrategyB. They represent concrete implementations for feedback control strategies.

Figure 11-10. Instantiating the FeedbackStrategy pattern.

The FeedbackObserver invokes the control routine of the Feedback that applies the feedback control strategy required from the component. The Feedback class interacts with the FeedbackSubject of the observer pattern in the error calculation component and invokes its notify() procedure. This establishes the link between the feedback component and the error calculation component.

We note that the participant names that are popular and frequently used in the pattern design are now domain-specific names. Two features can help the designer keep track of the patterns. First, the three model diagrams—Pattern-Level, Pattern-Level with Interfaces, and Detailed Pattern-Level—provide documentation of the framework as a composition of patterns. Second, with the appropriate tool support, the renaming process is not an editing process. In editing we simply change the names, and the old names are lost. But in the renaming process of a class, the tool support for POAD should provide a system with memory to keep the history of the changed name specifically in pattern instantiation, as discussed in Chapter 16.

Developing an Initial Class Diagram

From the Detailed Pattern-Level diagram, we use pattern interfaces and the instantiated details of pattern internals to construct a UML class diagram. This is a simple process of combining the domain-specific details discussed in the previous section with the Detailed Pattern-Level diagram. The class diagram developed at this phase is an initial step to develop the static design model of the pattern-oriented framework. We convert the class/class interface relationship between patterns by tracing each of the two class interfaces to the internal class of each pattern. These internal classes will have a UML-association relationship. We convert the class/operation interface relationship by tracing each interface operation to the class that implements that interface operation. The class is traced to the pattern's internal class, and hence a class relationship is established between internal classes of the two interfacing patterns. We convert the operation/operation interface relationship by tracing each interface operation to the internal class of the pattern. The internal classes of the two interfacing patterns will have a class-association relationship. As a result, the design is now viewed as a UML class diagram, as illustrated in Figure 11-11.

Figure 11-11. The initial class diagram of the feedback design framework.

It can be easily recognized that the patterns are still notable in the class diagram as shown by the dotted boxes around the classes. We recall that we do not discard or swap away earlier diagrams. As part of POAD, all the models in Figures 11-3 through 11-11 are saved as analysis and design models. It is the role of a tool support to save these models and provide the necessary traceability mechanisms between them.

How About Code Generation?

In the above process, we do not generate code at each level and keep it in synchronization with other levels. The process is mostly about manipulating the design elements, such as patterns, classes, and methods, at that analysis and design level. Code generation comes later, after the refined design is complete. It is also unnecessary to generate code from the initial class diagram because in the following phases the designer will change the design of the system by merging and grouping several classes together. So, if you are eager to see code samples, wait until you have completed the following phase.

Design Optimization

The class diagram obtained from gluing patterns together at the high-level design is neither dense nor profound, because we just strung the patterns together. It has many replicated abstract classes due to using multiple instances of the same pattern. For example we used the FeedbackStrategy and the FeedforwardStrategy instances of type Strategy pattern. It also has many classes with trivial responsibilities because many classes are there for forwarding messages to internal participants of the pattern. Therefore, in this step we use reduction and grouping mechanisms to optimize the UML design diagrams obtained initially in the previous step.

Reduction

In this step the complexity of the framework is reduced by eliminating replicated abstract classes. A pattern has one or more abstract classes. Since the same pattern type is used in more than one instance, we expect to find similar abstract classes. An abstract class, in its sense, should appear once and be subclassed according to concrete implementations. From the above figures, the Observer pattern is used in the feedback component and in the error component. Thus, the classes AbstractObserver and AbstractSubject are replicated. Similarly, the abstract class AbstractController of the strategy pattern used in the feedforward and feedback components. Therefore, the replicated classes are eliminated, and only one common version of the abstract class is used.

This step is not usually applicable to all designs, because the interfaces offered by abstract classes may substantially differ, and hence we cannot merge these two abstract classes into one class. However, for the feedback control system this is feasible. In general, this is an activity that the designer might consider as part of the development process.

Grouping

In this step more optimization in class usage is projected by merging concrete classes together depending on their interaction and responsibilities. This step brings the framework's class diagram to a more reduced form; however, it mainly depends on the framework designer's skills. From Figure 11-11, we find that the classes FeedbackObserver, FeedbackSubject, and Feedback perform highly related functions, which are summarized as receiving measurement notification, applying control strategy, and notifying the error component that the feedback data is ready. Instead of implementing a primitive function in each class, we can merge these three classes into one class, FeedbackSubjectObserver, which carries out the responsibilities of the three classes.

As a result of the reduction and grouping activities, the design becomes more dense and, depending on the designer's skills, more profound. Figure 11-12 illustrates the refined class diagram of the framework

Figure 11-12. The refined class diagram for the feedback design framework.

It could become difficult to identify the patterns at this level because the design is now represented in terms of domain-specific classes. This problem has always existed in many techniques that use patterns directly at the class diagram level without developing higher-level design models. POAD has one particular advantage. When applying POAD, the designer keeps all the models developed throughout the development lifecycle. These models are traceable bottom-up from the class level (Figure 11-12) to the pattern level (Figure 11-3) and top-down from the pattern level to the class level. This traceability can be automated in a design environment.

As an example of top-down traceability, we can identify the pattern participants in the above class diagram as follows:

·         FeedforwardStrategy:Strategy is composed of the classes Controller, AbstractController, ControlStrategyA, and ControlStrategyB.

·         FeedbackObserver:Observer is composed of the classes AbstractSubject, AbstractObserver, MeasurementSubject (a concrete subject), and FeedbackSubjectObserver (a concrete observer).

·         ErrorObserver:Observer is composed of the classes AbstractSubject, AbstractObserver, FeedbackSubjectObserver (a concrete subject), and ErrorObserver (a concrete observer).

·         FeedbackStrategy:Strategy is composed of the classes FeedbackSubjectobserver (the controller), AbstractController, FBControlstrategyA, and FBControlstrategyB.

·         Blackboard:Blackboard is composed of the Blackboard, DataHolder, and the inherited data subclasses Measureddata, FeedbackData, and ErrorData, which are information data classes used by MeasurementSubject, FeedbackSubjectObserver, and ErrorObserver.

As an example of bottom-up traceability, we find that the class FeedbackSubjectObserver is a common participant in multiple patterns:

·         It is the observer for MeasurementSubject in the FeedbackObserver. It is notified by changes in the MeasuredData.

·         it acts as a controller in the FeedbackStrategy that invokes a concrete control strategy to be applied on the FeedbackData.

·         it acts as a subject for the ErrorObserver. It notifies the observer of changes in the FeedbackData.

The following Java code sample shows the FeedbackSubjectObserver class, which has become the integration of the above three classes coming from three different pattern instances. We specifically note the following:

1.    The update() method has code that belongs to three roles: the role of an Observer in the FeedbackObserver pattern instance, the role of a Subject in the ErrorObserver pattern instance, and the role of a Context in the FeedbackStrategy pattern.

2.    The concrete observers use the pull mode in which the observer pulls data from the subject by calling its getState() method after the observer has been notified that a change has occurred in the subject data.

3.    To overcome the problem of multiple inheritances in Java, we defined AbstractSubject as an abstract class and AbstractObserver as an interface. FeedbackSubjectObserver implements the AbstractObserver interface to provide the observer role and extends AbstractSubject to provide the subject role. Other implementations are also possible.

public class FeedbackSubjectObserver extends AbstractSubject implements AbstractObserver
{
   protected AbstractController FBController;
   protected DataHolder feedbackData;
   protected Blackboard myBlackboard;
 
 
   /**
   */
   public void update(AbstractSubject changedSubject)
   {
       // When notified, get the data from the measurement subject
       // Role. Playing the role of a concrete observer class in an
       // Observer
       DataHolder measuredData;
       measuredData = changedSubject.getState();
 
       // Role.  Playing the role of a context class in a Strategy pattern
       FBController.FBApply(measuredData ,feedbackData);
       myBlackboard.setData(feedbackData);
 
 
       // Role. Playing the role of a concrete subject class in another
       // Observer
       notifyObservers();
 
   }
 
 
   /**
    */
   public DataHolder getState()
   {
       return feedbackData;
   }
   public FeedbackSubjectObserver()
   {
       feedbackData = new DataHolder();
   }
 
   public void setFBController(AbstractController aController)
   {
       FBController = aController;
   }
 
   /** A setter method for the storage object
   */
   public void setBlackboard(Blackboard theBlackboard)
   {
       myBlackboard = theBlackboard;
   }
 
}

Through the reduction and grouping steps, we combine pattern participants into single, domain-specific class. This has reduced the traceability of the patterns in the refined class diagram, a problem that has been identified by Soukup (1995) and Bosch (1998b) as a result of lack of patterns support in OO designs. But this may not be perceived as a major problem in constructing pattern-oriented designs and frameworks using POAD, because

·         The framework is still documented using higher-level pattern diagrams that the designer can use in addition to the refined class diagram. Traceability between these diagrams and class diagrams could be supported by tools that automate the traceability process.

·         When referring to the refined class diagram, the framework user is interested in the design of the framework in terms collaborating classes. Knowing how patterns were used to build it may be of less interest at that level, especially when the designer is concerned about implementation of these classes. The higher-level design models in terms of pattern diagram are of more interest to the framework designer/maintainer because it is easier for the framework maintainer to work on a design with fewer design constructs (patterns) and more comprehensive documentation (pattern documentation templates) than to work on many design constructs (classes) whose documentation is not as rich.

Up to this point, POAD provided some design models that utilize UML capabilities to compose patterns. It does not provide all the models that the designer needs to take this model into the implementation phase. To proceed with the development of the detailed class diagram, we can further analyze the refined class diagram using other UML models, such as statecharts and sequence diagrams. For instance, the dynamics of the refined class diagram can be studied using a UML collaboration diagram. Figure 11-13 illustrates a collaboration diagram to clarify the role of objects in the framework. Using this and other collaboration models, the designer can start defining message parameters, data types, additional methods, and so on.

Figure 11-13. A collaboration diagram for the feedback design framework.

As a result of analyzing the framework using interaction diagrams, collaboration diagrams, statecharts, and other UML models, we are able to add more design details to the refined class diagram of Figure 11-12. This could mean adding member functions for the framework classes, other properties or attributes to these classes, and references (pointers) in each class that implement the association relationships between that class and other classes in the framework. For example, from Figure 11-13 we added a MeasurePlant() method in the MeasurementSubject class and two methods in the ErrorObserver class, Analyze() and GetInput(). Figure 11-14 illustrates a more detailed class diagram for the framework.

Figure 11-14. A detailed class diagram for the feedback control framework.

Example: Quality Control in Production Lines

In this section, we illustrate a simple example for instantiating the framework in a quality-control system for a beverage bottle production line. In such systems it is usually required to distinguish defective bottles based on certain quality criteria defined by the system user. The system uses feedback data taken form actual measurements of the bottle under inspection to determine whether or not the criterion is met.

System Description

In the production line of the beverage bottle system, it is required to check the cleanness of the bottles before filling them with the beverage. The objective is to pass the clean bottles that meet a certain criterion and reject dirty ones. Bottles are introduced into the system on a conveyer belt that transfers the bottle to the check unit at which the belt stops and measurements are taken. The measured data is processed to give a cleanness value that is compared to a reference input and fed to the forward controller system. According to the error data values, the controller will either pass the bottle or remove it from the belt to be recycled. In either case the belt is activated again to proceed with processing the next bottle. From a physical point of view, the system consists of a conveyer belt, a bottle detector, a check unit containing a camera for taking images of the bottles, and actuators that remove dirty bottles from the belt. See Figure 11-15.

Figure 11-15. An a feedback system for a beverage bottle system.

Application-Specific Considerations

To simplify the example, we assume the following:

·         The detector is a simple sensor that indicates the presence of the item.

·         The implementation of the controller's routines will not be discussed. The required interface modules will depend on the operating system, the hardware, and the type of the sensor.

·         The real-time behavior of the system is not discussed. Timing events may be required in other applications.

·         Introducing items to the conveyer belt and removing them are out of the scope of the framework. We are more concerned with the feedback control aspects.

Instantiation of the framework

The following sequence of events takes place in the system:

·         Bottles are fed to the conveyer belt, which stops as soon as a bottle is detected in the check area. Data collection is performed by recording an image of the bottle.

·         Analysis of the image is conducted using specific image-processing routines. Measurements of cleanness and other attributes are calculated.

·         The measured values are compared to reference inputs, and the error values are calculated.

·         The forward controller rejects or passes the item according to the error data.

As discussed in Chapter 7, we can instantiate the framework in a specific application starting from the pattern-level diagram or the class-level diagram. Since this is an illustrative example and the framework user is the same as the framework designer (i.e., we have enough knowledge of the details of the framework), we instantiate the framework by starting with the framework class diagram in Figure 11-12. Following a simple approach for instantiating the framework, we start with each class in the framework class diagram and identify which elements need to be added as application-specific (the framework hooks) and which elements belong to the framework's fixed design.

·         The MeasurementSubject. The function of this class is to measure the real-world data, which is the camera image taken of each bottle. The details of the image size and resolution are specified at the detailed design. The MeasurePlant() method is added to control the camera and record the image.

·         The MeasuredData. It has the variables required to keep the instantaneous image of the bottle.

·         The FeedbackSubjectObserver. This class handles the image taken of the bottle by invoking the FBApply method of the attached feedback control strategy FBControlStrategyA.

·         The FBControlStrategyA. The necessary image-processing algorithm is implemented in this class. The FBApply() method implements an image-processing algorithm to detect the cleanness of the bottle and may also take some measurements of the image, such as width or height. The result of the algorithm is stored in a FeedbackData object.

·         The FeedbackData. The necessary feedback data, such as the percentage of cleanness or the width, is represented in an object of this type.

·         The ErrorObserver. After being notified by the FeedbackSubjectObserver, the object of this class gets the reference input data specified by the system user using the GetInput() method and compares it to the FeedbackData; the result is stored in an ErrorData object.

·         The Controller class. The controller invokes the forward control strategy, which, depending on the cleanness percentage error, either removes the item from the belt via RemoveItem() if defective or passes the item via PassItem() if it is acceptable. The Apply() method also invokes the StartBelt() procedure.

·         When the belt conveys another bottle to the check unit, the detector initiates an interrupt to invoke the StopBelt() method of the MeasurementSubject.

By adding these application-specific methods to the class diagram model of Figure 11-12, we obtain Figure 11-16, which illustrates an instantiated class diagram for the application.

Figure 11-16. The class diagram of the beverage bottle framework.

From this example, we deduce the following:

·         The instantiation process is direct and simple. The framework user does not add any new classes. She adds only application-specific methods and data types.

·         Decreasing the dependencies between several parts of the framework adds simplicity to the design and increases the flexibility of using and instantiating the framework.

·         The strategy for processing the image of the bottle is transparent to other design classes. This transparency is made possible by using the Strategy pattern.

Sample Implementation

This section contains a complete Java code implementation for the refined class diagram design of the feedback control framework. The implementation illustrates how the framework works for a hypothetical simple speed-control system. To simulate the system, we created a plant object that plays the role of speed sensor, which is controlled by a timer, and the timer triggers new data into the system. The input control data is adjusted by the user, and the speed converges to the requested speed. The complete model is shown in Figure 11-17.

/************************************
 *
 *      AbstractController .java
 *
 ***********************************/
public abstract class AbstractController
{
   protected Plant thePlant;
   public abstract void apply(DataHolder errorData);
   public abstract void FBApply(DataHolder inputData, DataHolder outputData);
   public AbstractController()  {  }
   public void setPlant(Plant aPlant)  { thePlant = aPlant; }
}
 
/************************************
 *
 *      ControlStrategyA.java
 *
 ***********************************/
public class ControlStrategyA extends AbstractController
{
   public ControlStrategyA() {  }
   public void apply(DataHolder errorData)
   {
       // This controller implements the forward strategy
       // Add your control strategy here
       // Get the error value
       int value  = errorData.getValue();
       // A simple control strategy
       if(value < 0)
       {
           // Apply control to the plant
          thePlant.control(true);
       }
       else if(value > 0)
       {
           // Apply control to the plant
          thePlant.control(false);
       }
       else // finally reached the requested speed
       {
        System.out.println("Plant is now regulated");
       }
   }
   public void FBApply(DataHolder inputData, DataHolder outputData)
   {
   // This controller only implements the feedforward strategy
   // Nothing goes here.
   }
}
 
/************************************
 *
 *      FBControlStrategyA.java
 *
 ***********************************/
public class FBControlStrategyA extends AbstractController
{
   public FBControlStrategyA() {  }
   public void apply(DataHolder errorData)
   {
       // This strategy does not provide a feedforward
       // control technique, only feedback control algorithms
   }
   public void FBApply(DataHolder inputData, DataHolder outputData)
   {
       // Your feedback control strategy goes here.
       // Takes the input data, processes it, and
       // returns an output data object
       // pass it as it is for this prototype
       outputData.setValue(inputData.getValue());
   }
}
 
/************************************
 *
 *      Controller.java
 *
 ***********************************/
public class Controller
{
   protected AbstractController controlStrategy;
   protected Blackboard myBlackboard;
   protected DataHolder inputData;
   public Controller()
   {
       inputData = new DataHolder();
       inputData.setValue(65);
       // use 65 miles per hour just as an example
   }
   public void control(DataHolder feedbackData)
   {
       // Calculate the error
       DataHolder errorData = new ErrorData();
       errorData.setValue(feedbackData.getValue() - inputData.getValue() );
       // Save it in the storage
       myBlackboard.setData(errorData);
       // Apply forward control strategy
       controlStrategy.apply(errorData);
   }
   public void setInputData(DataHolder newInputData)
   {
       inputData = newInputData;
   }
   public void setBlackboard(Blackboard theBlackboard)
   {
       myBlackboard = theBlackboard;
   }
   public void setStrategy(AbstractController aStrategy)
   {
       controlStrategy = aStrategy;
   }
}
 
/************************************
 *
 *      AbstractObserver.java
 *
 ***********************************/
public interface AbstractObserver
{
   public void update(AbstractSubject changedSubject);
}
 
/************************************
 *
 *      AbstractSubject.java
 *
 ***********************************/
import java.util.Vector;
public abstract class AbstractSubject
{
   protected Vector observers;
   public void attach(AbstractObserver anObserver)
   {
       observers.addElement(anObserver);
   }
   public void detach(AbstractObserver anObserver)
   {
       observers.removeElement(anObserver);
   }
   public void notifyObservers()
   {
       int size = observers.size();
       for(int i = 0 ; i < size; i++ )
       {
           ((AbstractObserver) observers.get(i)).update(this);
       }
   }
   public abstract DataHolder getState();
   public AbstractSubject()
   {
    observers = new Vector(2);
   }
}
 
/************************************
 *
 *      ErrorObserver.java
 *
 ***********************************/
public class ErrorObserver implements AbstractObserver
{
   private DataHolder errorData;
   protected Blackboard myBlackboard;
   public Controller theController;
   public ErrorObserver()
   {
       errorData = new ErrorData();
   }
   public void setBlackboard(Blackboard aBlackboard)
   {
       myBlackboard = aBlackboard;
   }
   public void setController(Controller aController)
   {
       theController = aController;
   }
   public void update(AbstractSubject changedSubject)
   {
       errorData = changedSubject.getState();
       theController.control(errorData);
   }
}
 
/************************************
 *
 *      FeedbackSubjectObserver.java
 *
 ***********************************/
public class FeedbackSubjectObserver extends AbstractSubject implements AbstractObserver
{
   protected AbstractController FBController;
   protected DataHolder feedbackData;
   protected Blackboard myBlackboard;
   public FeedbackSubjectObserver()
   {
       feedbackData = new DataHolder();
   }
   public void setFBController(AbstractController aController)
   {
       FBController = aController;
   }
   public void setBlackboard(Blackboard theBlackboard)
   {
       myBlackboard = theBlackboard;
   }
   public void update(AbstractSubject changedSubject)
   {
       // When notified, get the data from the
       // measurement subject
       // Role. Playing the role of a concrete observer class in
       // an Observer pattern
       DataHolder measuredData;
       measuredData = changedSubject.getState();
       // Role.  Playing the role of a context class
       // in a Strategy pattern
       FBController.FBApply(measuredData ,feedbackData);
       myBlackboard.setData(feedbackData);
       // Role. Playing the role of a concrete subject class
       // in another Observer pattern
       notifyObservers();
   }
   public DataHolder getState()
   {
       return feedbackData;
   }
}
 
/************************************
 *
 *      MeasurementSubject.java
 *
 ***********************************/
public class MeasurementSubject extends AbstractSubject
{
   private DataHolder measuredData;
   protected Plant thePlant;
   public MeasurementSubject()
   {
       measuredData = new MeasuredData();
   }
   public void measurePlant()
   {
       measuredData.setValue(thePlant.getPlantMeasurement());
       notifyObservers();
   }
   public void setPlant(Plant aPlant)
   {
       thePlant = aPlant;
   }
   public DataHolder getState()
   {
       return measuredData;
   }
}
 
/************************************
 *
 *      Blackboard.java
 *
 ***********************************/
public class Blackboard
{
   protected DataHolder dataRecord[];
   public Blackboard() {   }
   public void setData(DataHolder newData)  {  }
   public DataHolder getData() {  return null; }
}
 
 
/************************************
 *
 *      DataHolder.java
 *
 ***********************************/
public class DataHolder
{
   protected int currentValue;
   public DataHolder()   {   }
   public int getValue()
   {
       return currentValue;
   }
   public void setValue(int newValue)
   {
       currentValue = newValue;
   }
}
 
/************************************
 *
 *      Plant.java
 *
 ***********************************/
import java.util.*;
public class Plant extends java.util.TimerTask
{
    // initial value of speed
   protected int currentSpeed = 60;
   // start with 60 miles per hour
   protected MeasurementSubject aSensor;
   public Plant(MeasurementSubject theSubject)
   {
       aSensor = theSubject;
       // At the beginning, just assume the speed is
       // around one of the values
       currentSpeed += ( Math.random() * 10 - 5);
   }
   public int getPlantMeasurement()
   {
    // randomize the speed value
       return currentSpeed;
   }
   public void control(boolean speedUp)
   {
       System.out.println("Current speed is: " + currentSpeed );
       System.out.println("Command received is : " + speedUp);
       if(speedUp) // speed up the car
       {
        currentSpeed++;
       }
       else
       {   // slow down
           currentSpeed—;
       }
   }
   public void run()
   {
    aSensor.measurePlant();
   }
}
 
/************************************
 *
 *      aClient.java
 *
 ***********************************/
public class aClient
{
   protected java.util.Timer flushTimer;
   protected Plant aPlant;
   protected MeasurementSubject aMeasurementSubject;
   protected AbstractController forwardControl;
   protected AbstractController feedbackControl;
   protected Blackboard aBlackboard;
   protected ErrorObserver errorObserver;
   protected Controller aController;
   protected DataHolder inputData;
   protected FeedbackSubjectObserver feedbackSubjectObserver;
   public aClient() {   }
    protected boolean isDone() {  return false;    }
    protected void run()
    {
        int returnResults = 0 ;
       // initialize the components
       returnResults = initialize();
       if(returnResults < 0 )
       {
        // Report error
           System.out.println("Cannot initialize the system");
           return;
       }
       // wait for user action to stop the system
       while(!isDone())
       {
           // sleep for a while and recheck again later
       }
   }
    public static void main(String[] argc)
    {
          new aClient().run();
    }
    protected int initialize()
    {
        aBlackboard = new Blackboard();
        aMeasurementSubject = new MeasurementSubject();
        aPlant = new Plant(aMeasurementSubject);
        aMeasurementSubject.setPlant(aPlant);
        forwardControl = new ControlStrategyA();
        forwardControl.setPlant(aPlant);
        feedbackSubjectObserver = new FeedbackSubjectObserver();
        feedbackControl = new FBControlStrategyA();
        feedbackSubjectObserver.setFBController(feedbackControl);
        feedbackSubjectObserver.setBlackboard(aBlackboard);
        aController = new Controller();
        aController.setStrategy(forwardControl);
        aController.setBlackboard(aBlackboard);
        errorObserver = new ErrorObserver();
        errorObserver.setBlackboard(aBlackboard);
        errorObserver.setController(aController);
 
        // Attaching observers to subjects
        aMeasurementSubject.attach(feedbackSubjectObserver);
        feedbackSubjectObserver.attach(errorObserver);
        // Set the input control value
        inputData = new DataHolder();
        inputData.setValue(65);
        aController.setInputData(inputData);
        flushTimer = new java.util.Timer();
        flushTimer.scheduleAtFixedRate(aPlant, 5000, 1000);
       return 0;
   }
}
Figure 11-17. A simple feedback control example.

Summary

In this chapter, we illustrated the applicability of the POAD process to develop design frameworks. Design frameworks developed using POAD are called pattern-oriented frameworks. The development process discussed in Chapter 7 is applied. A pattern-level is described as a higher design level of class diagram, which represents the framework in terms of patterns, components, and associations. Ordinary techniques represent the framework in terms of classes and their interaction. The approach of constructing a design framework using constructional design patterns as design components makes the framework easier to understand by providing different levels of abstraction and facilitating traceability between these levels.

In the following chapter, we discuss another example of applying POAD to develop a pattern-oriented design for simulating the behavior of waiting queues.

posted on 2007-06-28 18:17 哼哼 阅读(415) 评论(0)  编辑  收藏 所属分类: JAVA-Common

只有注册用户登录后才能发表评论。


网站导航: