CSC/ECE 517 Fall 2010/ch7 7e GP: Difference between revisions
No edit summary |
No edit summary |
||
Line 1: | Line 1: | ||
= Protected Variations pattern = | = Protected Variations Pattern = | ||
GRASP stands for General Responsibility Assignment Software Patterns and defines nine patterns, of which '''protected variations ''' is one [1]. | |||
<blockquote>The protected variations pattern protects elements from the variations on other elements (objects, systems, subsystems) by wrapping the focus of instability with an interface and using polymorphism to create various implementations of this interface.[1]</blockquote> | |||
== Description == | |||
What this means is that any points where one system (A) touches another (B) where the implementation of that other system (B) contains variations in it's implementation. A good example of this is a standard plugin system. For example, an author may develop a plugin for Microsoft Outlook (a popular enterprise mail application). The '''protected variations pattern''' means that plugin should be wrapped within some kind of protection that would insulate Outlook from errant behaviors of that plugin. It may be disastrous if the plugin generated some sort of error that caused the entire application to crash. | |||
== Solution == | |||
The solution to these problems is very dependent on the system being analyzed. The first step is to identify all of the points of predicted variation of instability. Then a developer needs to create a stable ''interface'' around those points [2]. This solution has many potential problems in and of itself. It assumes a developer is intimately familiar with the entire system and all potential points of variation or instability. Second, it assumes a developer knows the perfect way to insulate the system from the variation. Finally, it employs the word ''interface'' in a very broad sense. It may be a single component, like a very simple object or an external system, like a web server. | |||
=== Insulation === | |||
Protecting a system from variations requires a thorough understanding of how the system may be exploited by the variation and what potential problems may arise from such a variation. For example, a database driver subsystem within the Java language may be insulated by catching ''SQLException'' exceptions. | |||
<code> | |||
... | |||
try { | |||
variant_method(); | |||
} catch ( SQLException e ) { | |||
// Handle exception | |||
} | |||
... | |||
</code> | |||
However, what happens if the variant method generates an ''Exception'' that is not a subclass of ''SQLException''? In this scenario the ''Exception'' would bubble up out of the protected area. The obvious solution is to catch all ''Exception''s. | |||
<code> | |||
... | |||
try { | |||
variant_method(); | |||
} catch ( Throwable e ) { | |||
// Handle exception | |||
} | |||
... | |||
</code> | |||
However, this has potentially undesirable effects also. What happens if there is a system-level exception, such as the system runs out of memory? This seems like an exception that would need to be communicated to a higher level and not consumed in the protection scheme. | |||
The answer is, it depends. Generally there will be a documented contract within the ''interface'' of the variant. The protection then checks that these error conditions don't violate the contract. This is good design and should be part of a system's design regardless. | |||
== Examples == | |||
Before discussing a specific example, it is important to understand that the '''protected variations''' pattern is present in almost every aspect of design. Consider virtual machines, plugin containers, sandboxes, configuration files and operating systems as specializations of the this pattern [3]. Generally examples are classified by the type of mechanisms that have motivated the '''protected variations''' pattern in the first place. The following sections have followed those first described by Larman, but with updated examples. | |||
=== Data-Driven Designs === | |||
Any piece of data that is read into a system in order to affect the system's behavior in some manner. For example, in a Ruby on Rails application the ''database.yml'' file defines database drivers to load and configuration parameters for these drivers. Some drivers require the definition of a username and password, while others require the definition of a file path, it is up to the driver as to which parameters to require. | |||
=== Service Lookup === | |||
This specialization uses a lookup service as a stable interface that is used to find a particular implementation of a component through the use of something like a naming service. The classic example of this is the Java Naming and Directory Interface (JNDI), another example is the Ruby Gem system. Using standard commands through the stable gem tool allows a developer to lookup and install gems to perform potentially unstable operations. | |||
=== Interpreter-Driven Designs === | |||
The premise behind interpreter-driven designs is an interpreter loads a particular file and executes the commands within that file to affect some result. The stable interface being the interpreter and the variant the interpreted code file that is loaded. The Ruby interpreter is a great example of this. | |||
=== Reflective or Meta-Level Designs === | |||
This specialization refers to not designing a stable system to a particular interface or implementation, but introspecting that interface at run-time. For example, a Ruby class would have its methods introspected to find the correct implementation that would then be executed within another protection level. | |||
<code> | |||
class Variant_1 | |||
def vvv | |||
@very_very_verbose = true | |||
end | |||
end | |||
class Variant_2 | |||
def vv | |||
@very_verbose = true | |||
end | |||
end | |||
v = Variant_1.new # or Variant_2.new | |||
if ( v.methods.include? "vvv" ) | |||
m = v.method :vvv | |||
elsif ( v.methods.include? "vv" ) | |||
m = v.method :vv | |||
end | |||
protect_method_call( m.call ) | |||
</code> | |||
== Conclusion == | |||
When implemented correctly the '''protected variations''' pattern can provide stability to a system that needs to open itself up to potentially unsafe or unstable variations. The benefits include [3] | |||
* Extensions required for new variations are easy to add. | |||
* New implementations can be introduced without affecting clients. | |||
* Coupling is lowered. | |||
* The impact or cost of changes can be lowered. | |||
This pattern is the same as information hiding and the open-closed principal in almost every regard. | |||
== References == | |||
[1] http://en.wikipedia.org/wiki/GRASP_%28object-oriented_design%29 | |||
[2] http://web.cs.wpi.edu/~gpollice/cs4233-a05/CourseNotes/maps/class4/ProtectedVariations.html | |||
[3] Craig Larman, Applying UML and Patterns: An Introduction to Object-Oriented Analysis and Design and Iterative Development, Third Edition, Addison Wesley, 2004, Section 25.4. |
Revision as of 06:17, 28 November 2010
Protected Variations Pattern
GRASP stands for General Responsibility Assignment Software Patterns and defines nine patterns, of which protected variations is one [1].
The protected variations pattern protects elements from the variations on other elements (objects, systems, subsystems) by wrapping the focus of instability with an interface and using polymorphism to create various implementations of this interface.[1]
Description
What this means is that any points where one system (A) touches another (B) where the implementation of that other system (B) contains variations in it's implementation. A good example of this is a standard plugin system. For example, an author may develop a plugin for Microsoft Outlook (a popular enterprise mail application). The protected variations pattern means that plugin should be wrapped within some kind of protection that would insulate Outlook from errant behaviors of that plugin. It may be disastrous if the plugin generated some sort of error that caused the entire application to crash.
Solution
The solution to these problems is very dependent on the system being analyzed. The first step is to identify all of the points of predicted variation of instability. Then a developer needs to create a stable interface around those points [2]. This solution has many potential problems in and of itself. It assumes a developer is intimately familiar with the entire system and all potential points of variation or instability. Second, it assumes a developer knows the perfect way to insulate the system from the variation. Finally, it employs the word interface in a very broad sense. It may be a single component, like a very simple object or an external system, like a web server.
Insulation
Protecting a system from variations requires a thorough understanding of how the system may be exploited by the variation and what potential problems may arise from such a variation. For example, a database driver subsystem within the Java language may be insulated by catching SQLException exceptions.
...
try {
variant_method();
} catch ( SQLException e ) {
// Handle exception
}
...
However, what happens if the variant method generates an Exception that is not a subclass of SQLException? In this scenario the Exception would bubble up out of the protected area. The obvious solution is to catch all Exceptions.
...
try {
variant_method();
} catch ( Throwable e ) {
// Handle exception
}
...
However, this has potentially undesirable effects also. What happens if there is a system-level exception, such as the system runs out of memory? This seems like an exception that would need to be communicated to a higher level and not consumed in the protection scheme.
The answer is, it depends. Generally there will be a documented contract within the interface of the variant. The protection then checks that these error conditions don't violate the contract. This is good design and should be part of a system's design regardless.
Examples
Before discussing a specific example, it is important to understand that the protected variations pattern is present in almost every aspect of design. Consider virtual machines, plugin containers, sandboxes, configuration files and operating systems as specializations of the this pattern [3]. Generally examples are classified by the type of mechanisms that have motivated the protected variations pattern in the first place. The following sections have followed those first described by Larman, but with updated examples.
Data-Driven Designs
Any piece of data that is read into a system in order to affect the system's behavior in some manner. For example, in a Ruby on Rails application the database.yml file defines database drivers to load and configuration parameters for these drivers. Some drivers require the definition of a username and password, while others require the definition of a file path, it is up to the driver as to which parameters to require.
Service Lookup
This specialization uses a lookup service as a stable interface that is used to find a particular implementation of a component through the use of something like a naming service. The classic example of this is the Java Naming and Directory Interface (JNDI), another example is the Ruby Gem system. Using standard commands through the stable gem tool allows a developer to lookup and install gems to perform potentially unstable operations.
Interpreter-Driven Designs
The premise behind interpreter-driven designs is an interpreter loads a particular file and executes the commands within that file to affect some result. The stable interface being the interpreter and the variant the interpreted code file that is loaded. The Ruby interpreter is a great example of this.
Reflective or Meta-Level Designs
This specialization refers to not designing a stable system to a particular interface or implementation, but introspecting that interface at run-time. For example, a Ruby class would have its methods introspected to find the correct implementation that would then be executed within another protection level.
class Variant_1
def vvv
@very_very_verbose = true
end
end
class Variant_2
def vv
@very_verbose = true
end
end
v = Variant_1.new # or Variant_2.new
if ( v.methods.include? "vvv" )
m = v.method :vvv
elsif ( v.methods.include? "vv" )
m = v.method :vv
end
protect_method_call( m.call )
Conclusion
When implemented correctly the protected variations pattern can provide stability to a system that needs to open itself up to potentially unsafe or unstable variations. The benefits include [3]
- Extensions required for new variations are easy to add.
- New implementations can be introduced without affecting clients.
- Coupling is lowered.
- The impact or cost of changes can be lowered.
This pattern is the same as information hiding and the open-closed principal in almost every regard.
References
[1] http://en.wikipedia.org/wiki/GRASP_%28object-oriented_design%29
[2] http://web.cs.wpi.edu/~gpollice/cs4233-a05/CourseNotes/maps/class4/ProtectedVariations.html
[3] Craig Larman, Applying UML and Patterns: An Introduction to Object-Oriented Analysis and Design and Iterative Development, Third Edition, Addison Wesley, 2004, Section 25.4.