Design Patterns, Singleton Implementation, and Anti-Patterns

Course Administrative Updates and Model Validation

  • Assignment Specification Update:

    • A small bug was identified in the assignment specifications.
    • An updated version of the assignment specification document was uploaded yesterday afternoon (prior to 4:00 PM).
    • Students who downloaded the specification prior to 4:00 PM must re-download the updated version to ensure compliance with corrected requirements.
  • AI-Assisted UML Model Validation:

    • Software requirements can be processed using AI models (such as GitHub Copilot or ChatGPT) to automatically generate PlantUML syntax.
    • PlantUML syntax can be pasted directly into the PlantUML web interface to render standard UML diagrams.
    • PlantUML Visual Elements:
    • Green dots: Represent public methods.
    • Red squares: Represent private methods or attributes.
    • Comparative Analysis of AI Outputs:
    • Copilot Output: Generates detailed class hierarchies, abstract classes, associations, and explicit function declarations. While detailed, it carries a risk of overengineering.
    • Historical ChatGPT Benchmark (Judy's Evaluation): Produced a starkly contrasting structure with zero inheritance relationships, relying entirely on bidirectional associations.
    • Evaluating AI UML Quality:
    • Verification against functional requirements and use cases.
    • Assessment of design quality (detecting lack of inheritance vs. overengineering).
    • Verification of semantic correctness and internal consistency.
  • Transitioning Models to Code:

    • Class Diagrams and State Diagrams guide the implementation of object-oriented structural design:
    • Class Diagrams: Establish class definitions, inheritance hierarchies, structural relationships, attributes, access levels, and method signatures.
    • State Diagrams: Dictate internal execution logic, state transitions, and control flow inside class methods.
  • Software Quality and Clean Code Attributes:

    • Key clean code attributes (reviewed from Week 3):
    • Naming conventions
    • Code structure
    • Comments
    • Documentation
    • Core software quality attributes essential for building durable, long-term software solutions:
    • Maintainability
    • Clarity
    • Testability
    • Portability

Foundations of Software Design Patterns

  • Code Reuse vs. Design Pattern Reuse:

    • Code Libraries: Importing external libraries allows direct code reuse, saving development time and providing confidence through community testing and quality validation.
    • Design Patterns: Represent structural and conceptual reuse rather than copying exact code snippets. They provide proven frameworks for recurring structural, behavioral, and class requirements.
  • Elements of a Pattern Context:

    • Context: The specific environment and scenario in which the problem occurs.
    • Problem: The recurring issue or challenge that needs to be solved.
    • Solution: The structural framework or strategy that resolves the issue.
  • Real-World Analogies of Pattern Recognition:

    • Rubik's Cube: Successful resolution requires recognizing the current spatial configuration (state) and executing specific predetermined series of rotations (moves) to transition to the solved state.
    • Chess: Master chess players identify specific board states and execute proven strategic moves or opening move sequences to secure positional advantages.
  • Origins and Definitions of Design Patterns:

    • Christopher Alexander Definition (Building Architecture): "Each pattern describes a problem which occurs over and over again in our environment and then describes the core of the solution to that problem in such a way that you can use the solution a million times over without ever doing it the same way twice."
    • The Gang of Four (GoF): In 1994, Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides published Design Patterns: Elements of Reusable Object-Oriented Software.
    • Historical Scope: Formulated using object-oriented languages (such as C++), documenting 23 fundamental design patterns categorized into three primary domains.
  • The Three Classifications of GoF Design Patterns:

    • Creational Patterns: Address object instantiation mechanisms. They abstract and hide creation logic rather than relying directly on direct instantiation (e.g., Book book = new Book()), providing creation flexibility.
    • Structural Patterns: Focus on class and object composition, organizing inheritance and interface hierarchies to form larger software structures.
    • Behavioral Patterns: Define how dynamic object instances interact, pass messages, and distribute algorithmic responsibilities among each other.
  • Classification Matrix by Type and Scope:

    • Design patterns are categorized by type (Creational, Structural, Behavioral) and scope (Class vs. Object):
    • Creational Class Scope: Factory Method
    • Creational Object Scope: Abstract Factory, Builder, Prototype, Singleton
    • Structural Class Scope: Adapter (Class)
    • Structural Object Scope: Adapter (Object), Bridge, Composite, Decorator, Facade, Flyweight, Proxy
    • Behavioral Class Scope: Interpreter, Template Method
    • Behavioral Object Scope: Chain of Responsibility, Command, Iterator, Mediator, Memento, Observer, State, Strategy, Visitor
  • Four Essential Elements of a Design Pattern:

    • 1. Name: A precise design handle used to communicate concepts and structures succinctly among developers.
    • 2. Problem: Explains the context, scenario, and explicit conditions required for the pattern to be applicable.
    • 3. Solution: An abstract template describing the components, relationships, responsibilities, and collaborations without committing to a specific concrete implementation.
    • 4. Consequences: The results, architectural impact, positive outcomes, and potential negative trade-offs associated with applying the pattern.
  • Process for Implementing Design Patterns:

    • 1. Thoroughly understand the pattern's intent, scope, and impact on system architecture.
    • 2. Study sample code and corresponding UML class/sequence diagrams.
    • 3. Evaluate existing codebase classes that will be altered or created by integrating the pattern.
    • 4. Explicitly define class structures, interfaces, and inheritance relationships.

The Singleton Design Pattern

  • Intent and Classification:

    • Category: Creational Pattern.
    • Scope: Object Level.
    • Intent: Ensure that a class has exactly one instance while providing a global point of access to that single instance.
  • Applicability Criteria:

    • There must be strictly one instance of a class required across the entire application runtime.
    • The single instance must be accessible to client components via a well-known, globally accessible access point.
    • The sole instance must be extensible through subclassing, allowing clients to utilize an extended instance without altering their codebase.
  • Core Implementation Steps:

    • 1. Private Constructor: Declare the default constructor as private to prevent client code from directly instantiating the class using the new operator.
    • 2. Private Static Instance Field: Store the single created instance in a private static field within the class.
    • 3. Public Static Creation Method: Implement a public static creation/accessor method (e.g., getInstance()) that invokes the private constructor on initial call and subsequently returns the cached instance.
  • UML Diagrammatic Representation:

    • Structure: Contains a private static instance variable, a private constructor, and a public static getInstance() method.
    • Graphical Indicator: Represented in class diagrams by a self-association loop pointing back to the class itself.
  • Initialization Strategies:

    • Lazy Initialization:
    • Implementation: The static instance field is set to null initially. Instantiation occurs inside getInstance() only when invoked for the first time (if (instance == null) { instance = new Singleton(); }).
    • Advantage: Conserves memory by delaying object instantiation until it is explicitly needed.
    • Disadvantage: Incurs computational overhead due to performing null checks on every invocation of getInstance().
    • Eager Initialization:
    • Implementation: The static instance field is instantiated immediately upon class loading (private static Singleton instance = new Singleton();).
    • Advantage: Saves computational execution time during runtime by eliminating conditional null checks inside getInstance().
    • Disadvantage: Consumes system memory immediately upon class loading, regardless of whether the instance is actually used.
    • Application Note: Either lazy or eager initialization may be utilized for implementing game state singletons (such as GameState in Assignment 2).
  • Concrete Example: Android Clicker Scoreboard Architecture:

    • Application Scenario: An Android game requires tracking how many times a user taps a moving target across fixed time intervals and displaying the updated score across multiple screen activities (Activity1_Start and Activity2_Play).
    • Failure without Singleton: Creating separate instances of AccountTracker in each activity results in isolated countValue state. Hits scored in Activity2_Play fail to reflect in Activity1_Start.
    • Solution with Singleton: Implementing AccountTracker as a Singleton ensures both Activity1_Start and Activity2_Play access the exact same underlying object instance, synchronizing click scores seamlessly across the app.

Singleton Controversies and Anti-Patterns

  • Criticisms of the Singleton Pattern:

    • Violation of Object-Oriented Principles: Suppressing public constructors breaks standard OOP creation mechanisms.
    • Global State Hazards: Introduces global state into an application, introducing hidden dependencies, architectural rigidity, and unexpected side effects.
    • Concurrency and Thread Safety Issues: Multithreaded environments risk generating multiple instances if lazy initialization is executed simultaneously without explicit synchronization mechanisms.
    • Java Reflection Vulnerability: Tools such as Java Reflection (java.lang.reflect) can override private constructor access modifiers, instantiating new objects and destroying singleton guarantees.
  • Definition of Anti-Patterns:

    • Common design practices or architectural choices that initially appear beneficial but ultimately prove detrimental to software quality, creating negative trade-offs that outweigh any initial advantages.
  • Specific Anti-Patterns Catalog:

    • 1. The Blob (God Object / Monster Class):
    • Description: A massive class or package that takes on excessive responsibilities, accumulating unrelated methods and data fields.
    • Identification Rule: If a class's functionality cannot be concisely summarized in a single simple sentence, it has accumulated too much responsibility and must be refactored.
    • Resolution: Decompose the class into smaller, single-responsibility components.
    • 2. The Childless Blob:
    • Description: A class that relies on extensive internal conditional checks, flags, and constraints to dictate behavior across operational roles rather than using polymorphism.
    • Concrete Example: A Person class storing a role string attribute ("author", "customer", or "librarian"). Methods like loanBook() rely on repetitive if (role == "customer") conditionals.
    • Drawbacks: Resulting code is unmaintainable, messy, and difficult to extend. Adding a new role requires modifying conditional logic across every single method in the class.
    • Resolution: Replace conditional roles with class inheritance and polymorphism by defining a base Person class and dedicated subclasses (Author, Customer, Librarian).
    • 3. The Golden Hammer:
    • Description: Forcing a familiar programming language, framework, design pattern, or tool onto every software problem regardless of suitability ("when all you have is a hammer, everything looks like a nail").
    • Visual Metaphor: A child's shape-sorting box where every distinct geometric block is forcibly shoved into the square hole.
    • Example: Using JavaScript universally for front-end web development, back-end server logic, native Android apps, and native iOS apps without evaluating native tools.
    • Resolution: Maintain continuous learning, objectively evaluate technology options, set personal biases aside, and choose the optimal tool for each specific problem.
    • Counter Anti-Pattern (Bleeding-Edge Instability): Continually swapping technologies and using unstable, experimental libraries purely because they are new, degrading codebase readability and team collaboration.