TP

Design Patterns Summary

What is Object-Orientation?

  • Abstraction: Ability to access objects while obscuring unnecessary details.

    • Simplifies code by hiding implementation complexities. This allows developers to focus on using objects without needing to understand their inner workings.

  • Polymorphism: Different classes implementing the same methods in their own ways.

    • Enables objects of different classes to respond to the same method call in a class-specific way. Example: A playSound() method can produce a different sound depending on whether it is called on a Dog object or a Cat object.

  • Inheritance: Classes inheriting methods and properties from superclasses, promoting code reuse.

    • Creates a hierarchy of classes where common attributes and behaviors are defined in a superclass and inherited by subclasses. This reduces redundancy and facilitates code maintenance.

  • Encapsulation: Bundling methods and properties within classes.

    • Controls access to these properties (e.g., public and private in Java), preventing direct manipulation of internal data and ensuring data integrity.

Strategy Pattern
  • Addresses the issue of sharing code between classes without using inheritance multiple times.

  • Encapsulates behavior into separate classes (strategies).

  • Allows algorithms to be selected at runtime.

  • Example: Animal attack behavior (biting or swooping).

  • Uses composition over inheritance.

Code Example: Animal and AttackBehavior

  • Animal class has a myAttack object of type AttackBehaviour.

  • AttackBehaviour is an interface with an attack() method.

  • Concrete attack classes (PoisonBiteAttack, SwoopAttack) implement AttackBehaviour.

  • Animal subclasses (e.g., Snake, Eagle) initialize myAttack with specific attack behaviors.

  • This allows each animal to have different attack without needing distinct subclasses to implement attack.

interface AttackBehaviour {
    void attack();
}

class PoisonBiteAttack implements AttackBehaviour {
    public void attack() {
        System.out.println("Bites with poison");
    }
}

class Animal {
    AttackBehaviour myAttack;

    public Animal(AttackBehaviour attackType) {
        this.myAttack = attackType;
    }

    public void doAttack() {
        myAttack.attack();
    }
}

class Snake extends Animal {
    public Snake() {
        super(new PoisonBiteAttack());
    }
}

public class Main {
    public static void main(String[] args) {
        Animal snake = new Snake();
        snake.doAttack(); // Output: Bites with poison
    }
}

Strategy Pattern Diagram

  • Classes: Animal, AttackBehaviour, PoisonBiteAttack, SwoopAttack.

  • Animals use AttackBehaviour to perform specific attacks.

Concept of Patterns
  • A pattern offers a reusable solution to a common problem.

  • The "Gang of Four" book is a well-known pattern catalog.

  • Patterns are categorized as:

    • Behavioral: define object interactions and responsibility distribution.

    • Creational: concern object creation processes.

    • Structural: deal with object composition into larger systems.

Key Design Principles Emphasized by the Strategy Pattern
  • Favor composition over inheritance.

  • Encapsulate what varies.

  • Program to an interface, not an implementation.

How to Describe a Pattern
  • Name: Standard term for the pattern.

  • Classification: Pattern type (e.g., creational, behavioral).

  • Intent: Brief description of the pattern's purpose.

  • Motivation: Illustrative example of the pattern's usage.

  • Applicability: Scenarios where the pattern is suitable.

  • Structure: Class diagram representing participating classes.

  • Participants: Roles and responsibilities of the classes involved.

  • Collaborations: Interactions between classes and objects.

  • Consequences: Pros and cons of using the pattern.

  • Implementation: Techniques and considerations for implementation.

  • Sample Code: Code snippets demonstrating pattern implementation.

  • Known Uses: Real-world examples of the pattern in use.

  • Related Patterns: Similar or complementary patterns.

Gang of Four Patterns
  • Creational:

    • Abstract Factory

    • Builder

    • Factory Method

    • Prototype

    • Singleton

  • Structural:

    • Adapter

    • Bridge

    • Composite

    • Decorator

    • Facade

    • Flyweight

    • Proxy

  • Behavioral:

    • Chain of Responsibility

    • Command

    • Interpreter

    • Iterator

    • Mediator

    • Memento

    • Observer

    • State

    • Strategy

    • Template Method

    • Visitor

Factory Method Pattern
  • Addresses issues with creating different environments (e.g., Jungle, Polar) without excessive code duplication.

  • Defines an interface for creating objects but lets subclasses alter the type of objects that will be created.

Problem Examples (Without Factory Method)

  • Multiple copies of environment setup code.

  • Conditional logic for environment creation, leading to code modification with each new environment type.

Class Diagram for Survival Environments (Factory Method Applied)

  • SurvivalGame (abstract class) defines the environment setup.

  • JungleSurvival and PolarSurvival (subclasses) create specific patches and barriers.

  • Patch and Barrier (abstract classes) for segments of the environment.

  • Concrete classes: Jungle, River, Snow, Crevasse.

Code Example

  • SurvivalGame has abstract methods createPatch() and createBarrier().

  • Subclasses (JungleSurvival, PolarSurvival) implement these methods to return the correct type.

abstract class SurvivalGame {
    public void setupEnvironment() {
        Patch patch = createPatch();
        Barrier barrier = createBarrier();
        System.out.println("Setting up " + patch.getDescription() + " and " + barrier.getDescription());
    }

    abstract Patch createPatch();
    abstract Barrier createBarrier();
}

class JungleSurvival extends SurvivalGame {
    @Override
    Patch createPatch() {
        return new Jungle();
    }

    @Override
    Barrier createBarrier() {
        return new River();
    }
}

public class Main {
    public static void main(String[] args) {
        SurvivalGame game = new JungleSurvival();
        game.setupEnvironment(); // Output: Setting up Jungle and River
    }
}

Design Principles in Factory Method Pattern

  • Open-Closed Principle: Classes should be open for extension but closed for modification.

    • New environments can be created without modifying existing code.

  • Concrete products are hidden from the creator class.

Abstract Factory Pattern
  • If the choice of factory is made at runtime, it becomes an Abstract Factory.

Decorator Pattern
  • Addresses the issue of adding properties to objects dynamically.

  • Avoids class explosion by using composition instead of inheritance.

  • Allows adding responsibilities to objects without modifying their structure.

Problem Example (Without Decorator)

  • Creating multiple subclasses for each property combination (e.g., WingedSerpent, FireBreathingLizard).

  • Violates Open-Closed Principle because new properties requires modification to existing classes.

Class Diagram Using Decorator Pattern

  • AbstractAnimal (implements Animal interface): Base class for animals.

  • Concrete animals: Hamster, Serpent, Lizard etc.

  • AnimalDecorator (abstract class): Implements Animal interface and holds a reference to Animal.

  • Concrete decorators: Winged, FireBreathing.

Code Example

  • AnimalDecorator implements the Animal interface and holds an instance of another Animal which it decorates.

  • getDescription and getWeight methods are overridden in the Decorator to add functionality.

  • Example: Animal h = new FireBreathing(new Winged (new Hamster("H")))

interface Animal {
    String getDescription();
    int getWeight();
}

class Hamster implements Animal {
    private String name;

    public Hamster(String name) {
        this.name = name;
    }

    @Override
    public String getDescription() {
        return name + " the Hamster";
    }

    @Override
    public int getWeight() {
        return 2;
    }
}

abstract class AnimalDecorator implements Animal {
    protected Animal animal;

    public AnimalDecorator(Animal animal) {
        this.animal = animal;
    }

    @Override
    public String getDescription() {
        return animal.getDescription();
    }

    @Override
    public int getWeight() {
        return animal.getWeight();
    }
}

class Winged extends AnimalDecorator {
    public Winged(Animal animal) {
        super(animal);
    }

    @Override
    public String getDescription() {
        return animal.getDescription() + ", Winged";
    }

    @Override
    public int getWeight() {
        return animal.getWeight() + 1;
    }
}

public class Main {
    public static void main(String[] args) {
        Animal h = new Winged(new Hamster("H"));
        System.out.println(h.getDescription() + " weighs " + h.getWeight() + " lbs");
        // Output: H the Hamster, Winged weighs 3 lbs
    }
}

Key Aspects of Decorator Pattern

  • Dynamic addition of responsibilities.

  • Avoids class explosion due to inheritance.

  • Open-Closed Principle is maintained.

Decorator Pattern in Java API

  • java.io.InputStream (Component).

  • java.io.FilterInputStream (Decorator).

  • Decorators: java.io.BufferedInputStream, java.io.CheckedInputStream.

  • ConcreteComponent: java.io.FileInputStream.

Design Principles
  • Favor composition over inheritance (Strategy, Abstract Factory)