Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Pro .NET 2.0 Code And Design Standards In CSharp (2006) [eng]

.pdf
Скачиваний:
34
Добавлен:
16.08.2013
Размер:
3.43 Mб
Скачать

146 C H A P T E R 7 D E S I G N D E V E L O P M E N T

The Standard: Design of Application Integration Layer

The standard acknowledges the design of an application integration layer, and its role is to satisfy domain and vendor requirements by providing a service that offers integration functionality that is customizable, transparent, and highly visible.

Design of Application Layer

The application layer is designed to represent the code encapsulated in domain and enterprise applications.

What

The application layer designs code to be coupled to the implementation of the application (domain or enterprise). Generally, code is designed specifically or specialized for a given application.

Where

An application layer is located in an enterprise design framework.

Why

By segregating application code from integration and services code, tight coupling is limited, which maximizes portability and minimizes the impact of change.

How

Application code is written or purchased to meet domain or enterprise requirements. Commonly, functionality may be accessed through ASP.NET, Windows, and Console application types.

The Standard: Design of Application Layer

The standard acknowledges the design of an application layer and that its role enables isolation of application code from integration and services code, which maximizes portability and minimizes the impact of change.

Horizontal and Vertical Design Methodologies

As discussed in the previous chapter, there is an enterprise-domain dilemma: Which functionality is enterprise and which is domain? While it was noted that an organization may determine a rule, it was also noted that addressing the question early (proactively), rather than later

C H A P T E R 7 D E S I G N D E V E L O P M E N T

147

(reactively), was preferable. Maybe the dilemma is associated with the way that we approach design. Generally, there are two ways to design functionality: from a layer or tier perspective (horizontally) and subsequently referenced by an application, or from an application perspective (vertically) and subsequently migrated through the layer hierarchy, where appropriate. With layer or horizontal design development, it is an intuitive part of the design process to consider whether design should be generalized or specialized; however, that is not the case when developing design from an application perspective, which is done vertically, where it is intuitive to design from a domain perspective. Perhaps a key to maximizing design and code reuse is to favor horizontal in preference to vertical design?

Horizontal Design Development

Horizontal design development refers to developing functionality within layers or tiers: each layer is seen as a distinct yet coordinated development.

What

Horizontal design development supports the notion that each layer is autonomous and collaborates with other layers through an interface. While functionality in all layers is designed to ultimately support applications, nevertheless an application is seen as a facade or a container through which layer functionality is accessed by clients.

Where

Horizontal design development occurs along each layer: the application, application integration, and enterprise services layers (and the other three layers, which have been excluded for convenience—see Figure 7-2 earlier).

Why

By regulating design development through layers, each layer may remain true to its inherent purpose and avoid becoming biased or coupled to any given application implementation or interface.

How

Typically, the driving force for development comes from the domains; however, the design and development of each of the layers is managed by developers who specialize in a given layer. As horizontal design follows a layer imperative, a domain application, for example, is seen as a consumer of services or client. Although layer developers are not developing for a given implementation, they need to be conscious that their development not only has to enable application developers to easily access functionality, but also needs to be presented in such a way that consuming layer functionality improves development efficiency. Figure 7-3 illustrates the autonomous nature of the layers that are developed horizontally and that an application is a facade through which functionality from the layers is accessed.

148 C H A P T E R 7 D E S I G N D E V E L O P M E N T

Figure 7-3. Horizontal design development

The Standard: Horizontal Design Development

The standard acknowledges horizontal design development as a method to regulate design development through layers, which seeks to ensure that the inherent purpose of the layer does not become biased or coupled to an application implementation.

Vertical Design Development

Vertical design development recognizes that functionality can be developed across layers to fulfill the requirements of a domain or enterprise application.

What

Vertical design development supports the notion that development commences in the application layer and progresses through the other layers, as appropriate. In other words, the layers are developed as part of, or as a consequence of, an application project and not autonomously.

Where

Vertical code development occurs across layers: the application, application integration, and enterprise services layers.

C H A P T E R 7 D E S I G N D E V E L O P M E N T

149

Why

By regulating design development from an application perspective, applications may be developed more quickly, and only functionality that is known to have an integration or enterprise value is migrated to those respective layers.

Caution An inherent risk in developing code in the application layer and then migrating it to the application integration and enterprise services layers is that the code may be too coupled to the domain. Excessive domain coupling is often quoted as a reason why so many applications are developed from scratch, because the code that is developed in applications commits the design to an implementation, and it is not viable to transpose it to an abstraction so that it may be reused.

How

An application is developed from a domain perspective with functionality being added or migrated to the application integration and enterprise services tiers, as appropriate. Generally, functionality is commenced in the application layer and moves up the hierarchy (see Figure 7-4). However, design development could commence in the enterprise services layer and progress downward. In such cases, it would mimic horizontal development and address the issue of excessive domain coupling or bias, which may improve design and code reuse.

150 C H A P T E R 7 D E S I G N D E V E L O P M E N T

The Standard: Vertical Design Development

The standard acknowledges vertical design development as a method to regulate domain development, but cautions that code and design may be excessively coupled to a domain, which may make it a poor candidate to migrate to other layers for reuse.

Object Collaboration

Designing applications is really about designing collaborations between objects; it is collaborating objects that perform the requirements of a domain.

Note Professor Trygve Reenskaug presents a most interesting discussion on the role of object collaboration; refer to the “Role Modeling” and “UML-VM” discussions (http://heim.ifi.uio.no/~trygver/index.html).

What

The functionality of an application may be expressed as a set of collaborations between objects. The ability of an object to collaborate is determined by its interface and its implementation, which impact its ability to form associations or relationships with other objects.

Where

Object collaboration occurs across and along application, application integration, and enterprise services layers.

Why

Collaboration is important because it is the exchange of messages, between objects, that yields the functionality of an application. Collaboration makes developing applications easier because, for example, a domain application developer can develop domain objects that leverage the functionality of security services objects without having to know the security implementation, only how to program against the interface exposed by the security services objects.

How

Collaboration between objects is developed through managing interface and implementation. Objects communicate through methods that are exposed in an interface and that provide functionality for a client object to consume. Often, we can overcome or negate problems that arise because an interface may be an impediment to a desired collaboration. For example, the Adapter design pattern uses an interposed object to overcome incompatible object interfaces and enables two incompatible objects to collaborate. Or consider the Chain of Responsibility design pattern, which enables objects to be arranged so that they may collaborate in a previously unforeseen manner: as an authoritative or specialized hierarchy.

C H A P T E R 7 D E S I G N D E V E L O P M E N T

151

Note The Adapter and Chain of Responsibility design patterns are discussed in Chapters 10 and 12, respectively.

The Standard: Object Collaboration

The standard acknowledges the importance of the role of object collaboration because it is the collaboration of exchanging messages between objects that yields the functionality of an application.

The Abstract-Interface Dichotomy

Often it is not obvious when to use class or interface inheritance to design the interface of an object: Why should we use an interface type when it has no functionality?

What

The dichotomy recognizes the problem that arises when designing code for objects that collaborate: Do we develop the interface as an abstract class or as an interface type? The dilemma is a manifestation of the underlying problem of choosing between class and interface inheritance.

Where

The dilemma occurs across application, application integration, and enterprise services layers.

Why

In nontrivial situations, it is not obvious whether an abstract class or an interface type is the best candidate for a base type: this really requires a case-by-case evaluation. The problem is complicated by the fact that C# supports single class inheritance, which means that in any hierarchy there can only be one base or super class. That contrasts with an interface type, which supports multiple inheritance, but does not implement functionality. There is a tendency to favor an abstract class, which may implement functionality, only to discover subsequently that the choice is inappropriate because it becomes too inflexible and cannot accommodate the collaborative demands of other types.

Note Abstract classes cannot be instantiated. They are conceptual; however, they may define functionality that a derived class may implement or specialize.

How

A key to the dilemma lies first in understanding the purpose of an interface type within the context of object collaboration. Once that is understood, then the role of the abstract class becomes ole that an object can perform, which

152C H A P T E R 7 D E S I G N D E V E L O P M E N T

is encapsulated in a list of generic methods that do not impose an implementation, and are therefore able to be utilized by a wide range of objects. For example, in the Model T domain, we may introduce an IServiceable interface that lists service functionality. A wide range of objects may inherit that role, for example, Engine, Radiator, and Brakes, and each service interface will be implemented differently. By analyzing collaborative roles, we can get a feel for the roles which are generic; a generic role suggests that the functionality is suited to be encapsulated in an interface type. Conversely, functionality that is dominate or common to a given class type is a likely candidate to be encapsulated in an abstract class.

The Standard: Object Collaboration (Abstract-Interface Dichotomy)

The standard acknowledges that it is not always obvious when to use class or interface inheritance to design the interface of an object. A key to the dilemma is that an interface type is used to signify a role that an object can perform—an interface type is suitable to perform generic roles.

The Composition-Inheritance Dichotomy

While objects are abstractions of real-life or logical entities and expose an interface through which other objects can collaborate, it is not always obvious how they should acquire that interface: through composition or inheritance.

What

The dichotomy recognizes the problem that arises when designing objects because there are two ways to acquire an interface, i.e., through composition or inheritance (an example is shown in the upcoming “How” section), and commonly it is not obvious which method to use.

Where

The dilemma occurs across application, application integration, and enterprise services layers.

Why

The dilemma arises because of the different abilities of class inheritance and composition to accommodate change in a domain. Class inheritance is more sensitive to change: it is statically defined at design time, and because a subclass implements functionality of the class hierarchy, it is additionally exposed to changes higher in the hierarchy. Composition, however, is less sensitive to change: it is defined at run time, and a consuming object may replace an object, at runtime, with a more suitable object.

How

A key to the dilemma is to be aware that, generally, class inheritance is more sensitive to change than is composition. GoF propose a principle: “Favor object composition over class inheritance.”1

1. Gamma, Helm, Johnson, and Vlissides, Design Patterns: Elements of Reusable Object-Oriented Software

C H A P T E R 7 D E S I G N D E V E L O P M E N T

153

Listing 7-1 and Listing 7-2 demonstrate the difference between acquiring functionality through composition and inheritance, respectively.

Listing 7-1. Composition

class ModelT

{

...

//Model T is composed of these two objects private object aObject;

private object bObject;

...

//ModelT leverages functionality //of contained objects

public string MethodOne()

{

return aObject.DoSomething();

}

public string MethodTwo()

{

return bObject.DoSomethingElse();

}

}

Listing 7-2. Inheritance

class Automobile

{

//functionality is developed within //the class hierarchy

public string MethodOne()

{

return "...";

}

}

class Car: Automobile

{

public string MethodTwo()

{

154 C H A P T E R 7 D E S I G N D E V E L O P M E N T

return "...";

}

.

}

class ModelT: Car

{

//ModelT inherits functionality //from the class hierarchy this.MethodOne(); this.MethodTwo();

}

In either example, the ModelT acquires the same functionality; however, it is the ease with which composition manages change that distinguishes it from inheritance. For example, to add new functionality, a ModelT built on composition merely adds another specialist object to do the new work; however, the ModelT built on inheritance requires the inheritance hierarchy to be modified. That may be problematic, as a change to a hierarchy has to consider all derived types that use the hierarchy and not just the ModelT.

The Standard: Object Collaboration (Composition-Inheritance Dichotomy)

The standard acknowledges that there is a dilemma when designing objects because there are two ways to acquire an interface: through composition or inheritance. A key to the dilemma is that class inheritance is more sensitive to change than is composition. GoF propose a principle: “Favor object composition over class inheritance.”

The Abstraction-Implementation Dichotomy

It is beneficial to use class inheritance to leverage the “free” functionality of a hierarchy; however, as the composition-inheritance dichotomy illustrated, class inheritance is sensitive to change. This, unfortunately, gives rise to another dilemma: When is it appropriate to reference a type as an abstract (interface) or as a concrete class (implementation)?

What

The dichotomy recognizes the problem that is associated with deciding whether to commit to reference a type through its interface (abstract class) or through its implementation (concrete class).

Where

The dilemma occurs across application, application integration, and enterprise services layers.

C H A P T E R 7 D E S I G N D E V E L O P M E N T

155

Why

The dilemma arises because at some point in time a design has to commit to an implementation; however, it needs to balance the cost of commitment against the need for flexibility in a dynamic domain.

How

A key to the dilemma lies in using polymorphism, which enables an object to be treated as an object (implementation) or as a member of its type (interface) at run time. Polymorphism preserves flexibility after an interface has committed to an implementation. The solution is articulated by GoF in a principle: “Program to an interface, not an implementation.”2 It works like this: a variable is declared as an abstract class or type (e.g., Mammal) and instantiated as a given subclass (e.g., Dog) of the type:

Mammal majorBarker = new Dog();

rather than

Dog majorBarker = new Dog();

This aids flexibility because it enables the object (majorBarker) to be referenced as an implementation (Dog) or through its interface (Mammal). Additionally, the concrete class (Dog) may be substituted by any class of the same type, for example, by a quiet Cat.

The Standard: Object Collaboration (Abstraction-Implementation Dichotomy)

The standard acknowledges that problems arise because at some point a design has to commit to an implementation; however, it needs to balance the cost of commitment against the need for flexibility in a dynamic domain. A key to the dilemma lies in using polymorphism, which enables an object to be treated as an implementation or as an interface at run time. GoF propose a principle: “Program to an interface, not an implementation.”

Design Patterns

It is not easy to orchestrate a society of objects in a collaboration; there are always problems at design time and run time, after an application has been deployed. Design patterns are commonly used as a design and maintenance tool to solve collaborative problems.

What

A design pattern is a methodology that cleverly arranges class interfaces and implementations to overcome collaborative problems.

2. Gamma, et al., Design Patterns, p. 18.

Соседние файлы в предмете Программирование на C++