Patterns Continued - Fowler.pdf

(1662 KB) Pobierz
356837039 UNPDF
Application Facades
Deep in the bones of Object-Oriented programming is notion of building a set of classes that mimics the
objects in the “real world”. That is we try to analyze the way people think about the world and let the classes
in our programs model the way an expert in a domain thinks. Many of the books on OO analysis and design
talk about developing this domain model. To do anything with the domain model we need to put information
into and out of it, typically through a Graphical User Interface (GUI). There is not so much written about that
part of object-oriented design.
This article serves many purposes, but the first purpose is to address this issue of the relationship between a
GUI and the underlying model. I hold to the principle that user interfaces should lie on the outside of the
system and be invisible to the classes that model the problem. This keeps the often varying UI functionality
away from the domain classes. The domain classes will model the domain, the UI classes handle the UI —
simple and separate responsibilities.
I go further than this, and divide the UI classes into two: a presentation class and an application facade class.
The presentation class is the class that handles all the UI work. The application facade class is responsible for
talking to the domain model and getting the information to the presentation class in exactly the form that the
presentation class requires. In this way the presentation class needs to know nothing about what is going on in
the model, it only handles the UI work.
UI
framework
presentation
application
facade
domain
testing
Figure 1 The general structure of packages and dependencies
Figure 1 shows a UML [UML] class diagram of the general structure of packages and dependencies I use. The
key points are:
The presentation package does not see the domain package
The application facade package does not see the UI framework
The testing package does not need to see the presentation package.
The benefits we get from this approach are:
We have split the UI classes into two sections with clear responsibilities for each. This makes each class
simpler and easier to understand and maintain.
356837039.003.png 356837039.004.png 356837039.005.png
We can choose to separate the tasks of coding the presentation and application facade classes. Those who
code the application facade need to understand the domain package but need know nothing about coding
UI classes, the presentation programmers need to know about the UI but not about the details of the
domain. If the domain classes and the UI framework are complex, as they often are, this makes it much
easier for programmers to be found and trained.
We can test most of the system without using the UI. Testing through the UI is generally awkward and it
is difficult to set up and maintain the testing scripts. By testing through the application facade only we
make it much easier to set up an automatic testing system which is essential to any well managed project.
There is still some testing of the UI that is needed, but the task is greatly reduced as in that testing we are
only concerned with the way the UI works, not how it interacts with the domain classes.
This article will explore how to do this in practice, with examples in Java. I discussed the principles of this in
chapter 12 and 13 of [Fowler], but did not provide any code examples. This article will should help dispel that
problem. For the domain model I chose to take some of the ideas of observation and measurement from
chapters 3 and 4 of [Fowler]. So this article also illustrates some examples of implementing those patterns.
This article also uses much the same material as that in the Java example in UML Distilled.
An Example Problem
Consider how a hospital’s computer systems might get at various observations they have made about a patient.
You could have a patient class with attributes for all the different types of observations (height, blood type,
heart rate, etc) but there would be thousands of such attributes: too many to have as attributes of a patient
class. So we can get around this by using the Observation and Measurement patterns from [Fowler]. For the
purposes of our discussion we want to be able record quantitative (height, 6 feet) and qualitative (blood group
A) statements about the patient. We also want to be able to assign qualitative statements depending on a
measurement. Thus is we record a person is breathing at a rate of 23 breaths a minute we should be able to
automatically make the qualitative statement that that is a fast breathing rate.
Phenomenon Type
Phenomenon
range : Range
0..1
*
1
1
*
*
Category Observation
isPresent : Boolean
*
<<abstract>>
Measurement
amount : Quantity
Observation
measurement
<<incomplete>>
*
category
<<incomplete, dynamic>>
1
Patient
Quantity
amount : Number
unit : Unit
Unit
Range
upper : Magnitude
lower : Magnitude
Figure 2 Conceptual UML diagram for the domain of this example
Figure 2 shows a conceptual model to support this kind of behavior. Before we dive into it I need to stress
that word conceptual . This model is not what the classes look like, rather it is an attempt to model the concepts
inside a doctor’s head. It is similar to the classes, but as we shall see we have to change them a bit in the
implementation. I have used several patterns here from [Fowler], specifically Quantity , Measurement, Observation,
Range , and Phenomenon with Range . I’ll discuss how the model works here, but I won’t discuss the justification
for why I’m doing that way, that I will leave to the book.
Say we want to record that Martin is breathing at 23 breaths per minute. We would do this by creating a
measurement object linked to the patient object that represents Martin. The phenomenon type of this
measurement object would be called “breathing rate”. The amount in a measurement would be handled by a
quantity object with amount of 23 and unit of “breaths per minute”.
To say that Martin’s breathing is fast we would create a category observation, again with Martin as the patient.
The category observation would be linked to a phenomenon of “fast breathing rate” which in turn would be
linked to the phenomenon type of “breathing rate”. If the “fast breathing rate” phenomenon has a range, we
should be able to automatically tell if it applies to a breathing rate of 23.
The way the Figure 2 works a single observation object can be both a measurement and a category
observation at the same time (since the generalization arrows carry different labels). Also a measurement can
begin life as a plain measurement and become a category observation as well later (indicated by the
{dynamic} constraint). The combination of the {abstract} constraint on observation and the {incomplete}
constraints on its subtypes implies that an observation can be either a measurement, or a category observation,
or both; but it may not be neither. This is a conceptual picture that we will not be able to directly implement
as Java does not allow us quite this flexibility in typing.
A model along the lines of this is very suitable for a hospital example because it will scale to the thousands of
phenomena that are observed in a hospital setting. For an individual use, however, it is not so suitable. An
0..1
*
1
1
*
*
1
356837039.006.png 356837039.001.png
individual use may want a simpler screen entirely, along the lines of that in Figure 3. Here the user does not
want to bother with knowing about observation objects, they just want to assign a value to some patient
attribute.
Figure 3 A sample screen showing a simpler view of patient information
Our task is to implement the model in Figure 2 yet provide a UI of the form of Figure 3. We will do this by
creating an application facade that converts from Figure 2 to a form ready for Figure 3 and a presentation
object that gives the display in Figure 3. I’m not making any claims about the practical usefulness of a screen
like Figure 3, the screen is purely a sample to discuss the software principles.
Implementing Quantity
Faced with this kind of situation many people would represent a heart rate with a number. I prefer to always
include units with this kind of dimensioned value, hence my use of the Quantity pattern. Implementing the
Quantity pattern is fairly straightforward in any object-oriented language.
public class Quantity {
private double _amount;
private Unit _unit;
Although we use a double for the internal amount we can provide constructors for different initialization
options. (Note that by convention I use a leading underscore on all fields.)
public Quantity (double amount, Unit unit) {
requireNonNull(unit);
_amount = amount;
_unit = unit;
public Quantity (String amountString, Unit unit) {
this (new Double(amountString).doubleValue(), unit);
public Quantity (int amount, Unit unit) {
this (new Double(amount).doubleValue(), unit);
protected void requireNonNull(Object arg) {
if (arg == null) throw new NullPointerException();
};
The quantity class needs a unit class, which for the purposes of this example need only know its name. A class
that has a name is a common need in these circumstances, so I have an abstract class, DomainObject, for it.
public DomainObject (String name) {
_name = name;
};
public String name ()
return _name;
{
};
};
};
356837039.002.png
};
protected String _name = "no name";
};
public class Unit extends DomainObject
Registrar
Another core behavior we will need is to get hold of specific objects without using global variables for
example the unit “breaths per minute”. I need unit to be an Entry Point [Fowler] for my objects so I can just
refer to the “breaths per minute” unit by going something like Unit.get(“breaths per minute”) .
I can implement this in two ways: either by having a static variable in the unit class, or by having a Registrar
object that manages these entry points. I prefer the Registrar as it is easier to manage. The Registrar is a
Singleton [Gang of Four] which manages several entry points.
public class Registrar
{
private static Registrar _soleInstance = new Registrar();
Each entry point is a Hashtable. Since the Registrar manages several entry points it keeps each entry point as
the value in a Hashtable indexed by some useful name, usually the name of the class that is acting as the entry
point.
public class Registrar {
private static Registrar _soleInstance = new Registrar();
private Dictionary _entryPoints = new Hashtable();
private void addObj (String entryPointName, DomainObject newObject) {
Dictionary theEntryPoint = (Dictionary) _entryPoints.get(entryPointName);
if (theEntryPoint == null) {
theEntryPoint = new Hashtable();
_entryPoints.put(entryPointName, theEntryPoint);
};
theEntryPoint.put(newObject.name(), newObject);
};
private DomainObject getObj (String entryPointName, String objectName) {
Dictionary theEntryPoint = (Dictionary) _entryPoints.get(entryPointName);
assertNonNull (theEntryPoint, "No entry point present for " + entryPointName);
DomainObject answer = (DomainObject) theEntryPoint.get(objectName);
assertNonNull (answer, "There is no " + entryPointName + " called " + objectName);
return answer;
private void assertNonNull(Object arg, String message) {
if (arg == null) throw new NullPointerException(message);
I use Lazy Initialization [Beck] if a client wants to store a value into an entry point collection that I have not
used yet. To make it a little easier to use the registrar I put some static methods on the class.
public static void add (String entryPoint, DomainObject newObject)
{
_soleInstance.addObj(entryPoint, newObject);
};
public static DomainObject get (String entryPointName, String objectName)
{
return _soleInstance.getObj(entryPointName, objectName);
};
However to make it easier for programmers I use methods on the appropriate classes to get values in and out
of the registrar:
public class Unit extends DomainObject {
public static Unit get (String name) {
return (Unit) Registrar.get("Unit", name);
};
public Unit persist()
{
};
};
Zgłoś jeśli naruszono regulamin