Prism uses proven design patterns to increase
developer productivity and to promote an architecture that supports
modularity and “evolvability,” the use of shared services and the
minimization of cross-team dependencies.
In this section we will briefly examine a few of the key patterns (Figure 1)
that are relevant to developing front-end logic for service
compositions. Note that these patterns can be applied regardless of
whether you are actually using the Prism library.
User Interface Patterns
The ability to combine individual views into a composite view is the underpinning of applying the Service Composability principle to the development of service-oriented presentation layers.
The following design patterns are directly related to enabling the
composite user experience.
Composite View [CJP]
The
original Composite [DP] pattern enables a client object to treat both
single components and collections of components identically. Composite
View [CJP] is a variant of Composite [DP] that composes views into tree
structures to represent part-whole hierarchies. This enables clients to
treat both individual views and compositions of views in a uniform
manner. This pattern is core to enabling a composite user experience.
Command [DP]
Command [DP] advocates that
objects be used to represent actions, so that a command object
encapsulates an action and its parameters. This pattern is useful for
establishing a decoupling of the invoker of the command and its
handlers. It also allows you to vary when and how an action is
fulfilled.
UI Mediator
UI Mediator enables timely interaction and feedback to the presentation layer from a
service-oriented solution, providing a consistent, interactive user
experience. This serves as the initial recipient of messages originating
from the presentation layer, with this mediation logic responding in a
timely and consistent manner regardless of the behavior of the underling
solution. UI Mediator is a very common design pattern when designing the presentation layer for service-oriented solutions.
Separated Presentation
This actually represents a
set of related patterns that promote the clean separation of the
responsibilities for the visual user interface of the underlying logic,
as well as presentation logic portions.
Patterns that implement separate presentation include:
Model-View-Controller [TR]
Model-View-Presenter [TAL]
Model-View-View-Model [JG] (also known as Presentation Model)
The original
Model-View-Presenter pattern was refined into the Supervising [PEA] and
Passive View [PEA] patterns. Almost all of these patterns separate out
the user interface from the business logic and the presentation logic.
Variations between the patterns exist because of the specific ways in
which these three capabilities interact with each other. Two of the
patterns commonly used with WPF and Silverlight are Presentation Model
[POS] and Supervising Presenter [PEA]. Let’s briefly examine why.
Presentation
Model [PEA] extracts out the user interface controls and visual state
(and behavior that is specific to the user interface) into the view
class. It does this while also pulling out the presentation layer
behavior and state into the presentation model class. The view acts as
an observer of the presentation model, and the presentation model acts
as an observer of the model (Figure 2).
By appropriately designing
the presentation model, developers can leverage the powerful
data-binding capabilities provided by WPF and Silverlight. With this
level of data binding, client-side application data automatically
reflects when the underlying data changes.
The Supervising Presenter
[PEA] pattern extracts into the view class the user interface controls,
visual state, and behavior that are specific to the user interface. This
is data bound directly to a model class that serves as the conduit for
the application’s domain data. The presentation behavior and state to
manage the interaction between the view and the model are extracted into
the presenter class (Figure 3).
To facilitate the application
of this pattern, the model must be designed to support data binding. By
appropriately designing this model, you can leverage the data-binding
capabilities provided by WPF and Silverlight.
Modularity Patterns
The next set of design patterns are core to enabling the dynamic discovery and composition of modules.
Separated Interface [PEA]
As
per its name, Separated Interface [PEA] is used to separate the
interface of an object from its implementation, usually by placing the
interface definition in a separate package. This pattern is useful for
attaining loose coupling by eliminating the need for a client that has a
dependency on an interface to be aware of the details of the concrete
implementation.
Plug-In [PEA]
The Plug-In [PEA] pattern
enables the concrete implementation of a class to be determined at
runtime. This removes the need for recompilation, either due to the
specific implementation that is used, or because of changes in the
implementation.
Event Aggregator [PEA]
This next pattern
channels events from multiple objects through a single object to
simplify registration for event subscribers. Event Aggregator [MPP] is
useful when there are many potential event sources (modules in a service
composition) promoting the decoupling of publishers and subscribers so
that they can evolve independently.
Inversion of Control [DP]
The Inversion of Control
[DP] pattern is used to enable extensibility in a class or library. In
this section we are primarily interested in the following two patterns
that are derived from Inversion of Control [DP].
Dependency Injection [PEA]
Dependency injection is a variation
of Inversion of Control [DP] that is used with the Prism library. It
promotes the decoupling of classes from their dependencies so that these
dependencies may be replaced or updated with minimal changes to the
classes themselves. During a class’s construction phase, it provides any
dependent classes to the class.
Because of this, the concrete
implementation of the dependencies can be changed more easily as the
system evolves. This better supports testability and evolvability of a
system over time. It further enables classes to depend on and use other
classes whose concrete implementation may not be available or known at
design-time. Furthermore, it decouples classes from being responsible
from managing the lifetime of their dependencies.
There are specific advantages to using a dependency injection container, because the container:
injects module dependencies into the module when it is loaded
is used for registering and resolving presenters and views
creates presenters and presentation models and injects the view
injects the composition services (such as the region manager and the event aggregator)
Service Locator [CJP]
Service Locator [CJP]
is another variation of Inversion of Control [DP] that allows classes
to locate specific services they are interested in without needing to
know who implements the service. Frequently, this is used as an
alternative to dependency injection, but there are times when a class
will need the service location instead of dependency injection (such as
when it needs to resolve multiple implementers of a service).