CSC/ECE 517 Fall 2012/ch2a 2w14 bb

From Expertiza_Wiki
Revision as of 02:54, 28 October 2012 by Yliu63 (talk | contribs) (→‎Factory)
Jump to navigation Jump to search

Introduction

The purpose of the wiki is to introduce and show some example about pattern fragility. The contents include definition of pattern fragility and what does the concept cover. We also give some examples of mistakes in code that spoil the benefits of design patterns.

Design Patterns

A design pattern is a general reusable solution to a commonly occurring problem within a given context in software design. A design pattern is not a finished design that can be transformed directly into source or machine code. It is a description or template for how to solve a problem that can be used in many different situations. Patterns are formalized best practices that the programmer must implement themselves in the application. Object-oriented design patterns typically show relationships and interactions between classes or objects, without specifying the final application classes or objects that are involved. Many patterns imply object-orientation or more generally mutable state, and so may not be as applicable in functional programming languages, in which data is immutable or treated as such.

Why Design Patterns

A design pattern is a pattern—a way to pursue an intent—that uses classes and their methods in an object-oriented language. Developers often start thinking about design after learning a programming language and writing code for a while. You might notice that someone else’s code seems simpler and works better than yours does, and you might wonder how that developer achieves such simplicity. Design patterns are a level up from code and typically show how to achieve a goal using a few classes. A pattern represents an idea, not a particular implementation.

Categorization of Patterns

Intent Patterns
Interfaces ADAPTER, FACADE, COMPOSITE, BRIDGE
Responsibility SINGLETON, OBSERVER, MEDIATOR, PROXY, CHAIN OF RESPONSIBILITY, FLYWEIGHT
Construction BUILDER, FACTORY METHOD, ABSTRACT FACTORY, PROTOTYPE, MEMENTO
Operations TEMPLATE METHOD, STATE, STRATEGY, COMMAND, INTERPRETER
Extensions DECORATOR, ITERATOR, VISITOR

Benefits of Design Patterns

Design patterns provide a way of encapsulating the experience of software developers in a form that can be communicated to other developers. They provide a higher level of abstraction than single classes and objects thus providing bigger building blocks in the construction of software designs.

The main benefits of design patterns are:

  • They encapsulate and codify design experience.
  • Provide a common vocabulary for software designers to use when communicating with their peers.
  • Enhance maintainability of software systems whose designs are documented with patterns.
  • Provide robustness to the design by copying or imitating proven design techniques.
  • Reuse at the design level.

At first glance some of these benefits may not seem very powerful, but upon further inspection maybe we can gain new insight into the true nature of their value. Let us just consider the fact that we have given a design pattern a common name. We can now communicate an entire design principle or concept with other software developers by just using the simple name of the pattern. In one fell swoop we have drastically reduced the effort and time needed for a developer to discuss a concept with another developer. Taken one step further we can discuss the patterns and their interactions in the system and illustrate the system architecture in few sentences. Grouping design concepts with common names facilitates communication among developers and raises the conversation to a higher level of abstraction.

Pattern Fragility

Symptoms of Rotting Design

There are four primary symptoms that tell us that our designs are rotting. They are not orthogonal, but are related to each other in ways that will become obvious.

The four symptoms are:

  • Rigidity -- the tendency for software to be difficult to change, even in simple ways.
  • Fragility -- the tendency of the software to break in many places every time it is changed.
  • Immobility -- the inability to reuse software from other projects or from parts of the same project.
  • Viscosity -- viscosity comes in two forms: viscosity of the design, and viscosity of the environment.

In following passage of this wiki page, we will discuss over 'Fragility' with some examples.

Definition of Pattern Fragility

Fragility is the tendency of the software to break in many places every time it is changed. Often the breakage occurs in areas that have no conceptual relationship with the area that was changed. Such errors fill the hearts of managers with foreboding. Every time they authorize a fix, they fear that the software will break in some unexpected way. As the fragility becomes worse, the probability of breakage increases with time, asymptotically approaching 1. Such software is impossible to maintain. Every fix makes it worse, introducing more problems than are solved. Such software causes managers and customers to suspect that the developers have lost control of their software. Distrust reigns, and credibility is lost.

Examples

Singleton -- the most overused pattern

Sometimes it's important to have only one instance for a class. For example, in a system there should be only one window manager (or only a file system). Usually singletons are used for centralized management of internal or external resources and they provide a global point of access to themselves. The primary purpose of the singleton is to guarantee that at anytime there is only one instance for a given class and provide a global reference to it.

The singleton pattern is one of the simplest design patterns: it involves only one class which is responsible to instantiate itself, to make sure it creates not more than one instance; in the same time it provides a global point of access to that instance. In this case the same instance can be used from everywhere, being impossible to invoke directly the constructor each time.

Well, the question is, is it safe to use a singleton class when it might be very tempting to do so? Obviously, the answer is negative. There is a general misconception about how Singletons should be used. Possibly, because it is true, that for having global state, it is better using Singletons, than just plainly global variables or class objects.

Some people see the Singleton as a justification for global state, along the lines of "If there's a pattern for it, it must be good". Well no, it isn’t. Global state is considered harmful. For a number of reasons, that even Singleton-misuse won't make go away, simply because: Singletons are NOT intended to provide global state!

The Singleton is a creational pattern. It is used to enforce, that a class be instantiated only once. What it basically does is, to give control over instantiation back to the programmer. This is what you sometimes need in languages with classical constructors (Java, C++ and such).

Factory

Create an interface for building an object, but let subclasses decide which class to instantiate. It allows a class to defer instantiation of subclasses. Factory pattern is one of the patterns that have being heavily abused. With as long as if --- else, go to the factory model, as long as the object involved in the creation of selective, go to the factory model. Factory pattern seems to have become the panacea to solve all. But let us look at some of the above contrast with the factory pattern, you simple code it? Wrong! The contrary, no increase in the scalability of the code at the same time, it increased the number of types of calls, an increase of the number of categories. This is not what we want to see!


One example is there is only one object to be instantiate. There was no concept of abstraction or extended classes,just plain old ClassX & ClassXFactory.

 public class A {
       public String str;
       public String getStr() {
              return str;
       }
}// End of class

public class ChildA extends A {
       public ChildA(String str) {
       System.out.println("Hello "+str);
       }
}// End of class

public class AFactory {
       public static void main(String args[]) {
              AFactory factory = new AFactory();
              factory.getA(args[0]);
       }

       public A getA(String str) {
              if(str!=NULL)
                     return new A(str);
              else
                     return null;
       }
}

When to use a Factory Pattern? The Factory patterns can be used in following cases:

1. When a class does not know which class of objects it must create.

2. A class specifies its sub-classes to specify which objects to create.

3. In programmer’s language (very raw form), you can use factory pattern where you have to create an object of any one of sub-classes depending on the data provided.

Visitor

Visitor pattern represent an operation to be performed on the elements of an object structure. In GoF's word, "It let you define a new operation changing the classes of the elements on which it operates."

Command

The Command design pattern encapsulates commands (method calls) in objects allowing us to issue requests without knowing the requested operation or the requesting object. Command design pattern provides the options to queue commands, undo/redo actions and other manipulations.

There are two extremes that a programmer must avoid when using this pattern:

1. The command is just a link between the receiver and the actions that carry out the request 2. The command implements everything itself, without sending anything to the receiver.

We must always keep in mind the fact that the receiver is the one who knows how to perform the operations needed, the purpose of the command being to help the client to delegate its request quickly and to make sure the command ends up where it should.

One example is:

 class FileDeleteCommand
   def initialize(path)
     @path = path
   end
   def execute
     File.delete(@path)
   end
 end
 fdc = FileDeleteCommand.new('foo.dat')
 fdc.execute

there is nothing simpler than just getting on with it: File.delete('foo.dat')

Conclusion

Reference