Software Architecture: Model Validation, Design Patterns, Singleton, and Anti-Patterns

Software Model Validation and Software Quality

  • Assignment Specification Update:

    • A bug was discovered in the assignment specifications, and an updated version was uploaded at approximately 4:00 PM4:00\,\text{PM} yesterday.
    • Any assignment specification downloaded prior to 4:00 PM4:00\,\text{PM} yesterday must be replaced with the updated version containing fixes.
  • Software Model Validation using AI Tools:

    • System requirements can be tested in AI tools such as Copilot to generate PlantUML syntax.
    • Pasting AI-generated PlantUML syntax into the PlantUML web tool renders complete Unified Modeling Language (UML) software diagrams.
    • PlantUML visual notation key:
    • Green dots represent public methods.
    • Red squares represent private methods.
    • UML features captured include class hierarchies, abstract classes, associations, attributes, access levels, and methods.
    • Comparative Analysis of AI Modeling:
    • An evaluation conducted a year or two prior by testing ChatGPT (via Judy) yielded a UML diagram that differed significantly from Copilot's output.
    • ChatGPT output evaluation: Exhibited poor design quality, completely lacking class inheritance and relying heavily on bidirectional associations.
    • Copilot output evaluation: Rendered class hierarchies with abstract classes and explicit associations, though it presented potential overengineering.
    • Strategic Use of AI in Software Engineering:
    • AI should be treated as a collaborative brainstorming teammate to validate UML diagrams against initial requirements.
    • Evaluation checklist for AI models: Verify whether the design is semantically correct, structurally consistent, well-designed, and capable of supporting all system use cases and requirements.
  • Software Quality and Clean Code Attributes:

    • Core clean code attributes reviewed from Week 3:
    • Structure
    • Naming conventions
    • Comments
    • Documentation
    • Fundamental software quality attributes ensuring long-term architectural stability across changing environments:
    • Maintainability: Ease with which software can be modified or corrected.
    • Clarity: Direct readability and comprehensibility of the codebase.
    • Testability: Facility with which the software can be verified through tests.
    • Portability: Ability of the software to operate across diverse environments.
    • Transition from Models to Implementation Code:
    • Class diagrams specify class hierarchies, inheritance, attributes, access modifiers, and method signatures.
    • State diagrams define the operational behavior and state transitions within method bodies.

Foundations of Software Design Patterns

  • Code Reuse vs. Design Pattern Reuse:

    • Code Reuse: Importing external libraries backed by active open-source communities to save development time while maintaining confidence in code quality and testing.
    • Design Pattern Reuse: Reusing high-level structural, class, or behavioral concepts and architectural strategies rather than duplicating verbatim lines of code.
    • Structural Components of a Pattern:
    • Context
    • Problem
    • Solution
  • Analogies for Pattern Recognition:

    • Rubik's Cube: Recognizing a specific physical state of the cube and executing a pre-determined sequence of rotations/moves to transition it into the target solved state.
    • Chess: Recognizing board states to execute proven tactical move sequences, positional strategies, and specialized opening patterns.
  • Historical Context and Definitions:

    • Christopher Alexander Definition: A construction designer who posited that a pattern describes a recurring problem in the environment along with the core solution, enabling the solution to be executed a million times over without doing it the exact same way twice.
    • The Gang of Four (GoF) Publication (19941994):
    • In 19941994, four authors published the seminal book Design Patterns.
    • Documented 2323 foundational design patterns centered around Object-Oriented Programming (OOP) using C++ concept examples.
  • The Four Essential Elements of a Design Pattern:

    1. Name: A single concise descriptor used to reference and communicate the design concept.
    2. Problem: Describes the context and specific conditions that must be present for the pattern to be applicable.
    3. Solution: An abstract structural template applied to resolve the underlying software problem.
    4. Consequences: The complete set of results, benefits, and trade-offs of applying the pattern (primarily positive, though negative consequences must be weighed).
    • Note: The original GoF book breaks pattern analyses down into 1313 detailed sections.
  • Categorization Matrix of Design Patterns:

    • The 2323 patterns are organized into 33 primary categories across 22 scopes (Class-level and Object-level):
    • Creational Patterns: Focus on object instantiation mechanisms, providing ways to create objects while hiding creation logic instead of directly invoking constructors (e.g., avoiding explicit book = new Book() calls) to enhance flexibility.
    • Structural Patterns: Focus on class and object composition, utilizing inheritance and interfaces to organize structure.
    • Behavioral Patterns: Focus on interaction and communication protocols between objects.
  • Implementation Workflow for Design Patterns:

    1. Understand the pattern thoroughly, including its context, applicability, and broader architectural consequences.
    2. Review reference code and online documentation (such as the Gang of Four pattern resources provided on Moodle).
    3. Analyze UML structural descriptions to recognize interaction arrangements.
    4. Define classes and inheritance hierarchies, accounting for pre-existing system classes that will be impacted.

The Singleton Design Pattern

  • Classification and Architectural Intent:

    • Pattern Classification: Object-level Creational Design Pattern.
    • Primary Intent: Ensure a class has exactly one instance while providing a global point of access to that instance.
  • Applicability Criteria:

    • Must be applied when exactly one instance of a class is required, accessible to all clients from a single well-known access point.
    • Must be applied when the single instance needs to be extensible via subclassing, enabling clients to utilize an extended instance without altering client code.
  • Step-by-Step Practical Implementation Mechanics:

    1. Declare the default constructor as private to prevent external objects from using the new operator on the class.
    2. Declare a private static field within the class to store the single cached instance.
    3. Implement a public static creation method (e.g., getInstance()) that invokes the private constructor internally on first execution and returns the cached static instance on all subsequent calls.
  • Comparison of Initialization Strategies:

    • Lazy Initialization:
    • The static instance variable is initialized to null by default.
    • The getInstance() method checks if the instance variable is null; if null, it executes the private constructor and assigns the object before returning it.
    • Advantage: Conserves memory by deferring object instantiation until the instance is explicitly required.
    • Disadvantage: Requires an explicit conditional null-check on every execution of getInstance().
    • Eager Initialization:
    • The static instance is initialized immediately upon class loading (e.g., private static Singleton instance = new Singleton();).
    • The getInstance() method directly returns the pre-instantiated static field without conditional checks.
    • Advantage: Saves computational overhead by eliminating the runtime null-check step per access call.
    • Disadvantage: Allocates system memory immediately upon class loading regardless of whether the instance is ever consumed.
  • UML Structural Key:

    • Identified by a self-referential association loop pointing back to the class itself.
    • Attributes: - instance: Singleton (private static field).
    • Constructors: - Singleton() (private constructor).
    • Methods: + getInstance(): Singleton (public static getter).
  • Practical Case Studies:

    • Android Tap-Target Game:
    • Application requires tracking user taps on a moving target over a fixed duration.
    • System components Activity1_Start and Activity2_Play both require access to the score tracker class CountTracker.
    • If two separate instances of CountTracker exist, their countValue attributes diverge, breaking the score display. Making CountTracker a Singleton guarantees both activities reference the exact same state.
    • Assignment 2 Architecture (GameState Class):
    • Class diagram contains private currentGame (or GameState), a private constructor GameState(), and a public static getInstance() method.
    • This structure confirms that GameState must implement the Singleton design pattern. Either lazy or eager initialization may be chosen.
  • Technical Criticisms and Limitations:

    • Violates pure Object-Oriented Design (OOD) standards by restricting standard instantiation and hiding constructors.
    • Introduces multi-threading race condition vulnerabilities (thread safety risks) if synchronization is omitted.
    • Subject to encapsulation failure via Java Reflection APIs, which can force access to private constructors and break uniqueness guarantees.
    • Introduces global state into application architecture, creating tight coupling and hidden dependencies.

Software Anti-Patterns

  • Definition and Overview:

    • Anti-patterns are common software development practices or design strategies that initially appear beneficial but ultimately yield negative consequences that far outweigh any short-term gains.
  • The Blob (or God Object):

    • Description: A class or package that takes on excessive responsibilities and accumulates unstructured logic.
    • Prevention Rule: If a class's functionality cannot be fully described in a single concise sentence, it possesses too much responsibility and must be refactored into multiple distinct classes.
  • The Childless Blob:

    • Description: A class that relies on extensive conditional branching (if/else or switch logic) checking role/type attributes to govern behavior, rather than using object-oriented polymorphism and inheritance.
    • Structural Example: A Person class containing a role attribute ("author", "customer", "librarian") where operations like loanBook() execute conditional branches checking if (role == customer).
    • Negative Consequences: Results in unmaintainable, fragile code. Adding a new role requires updating conditional statements across all methods in the codebase.
  • The Golden Hammer:

    • Description: The practice of universally applying a single familiar technology, programming language, design pattern, or framework to every software problem regardless of suitability (fitting every shape into a square hole).
    • Example: Indiscriminately applying JavaScript across web frontends, backend servers, Android apps, and iOS apps.
    • Prevention Strategy: Practice continuous professional upskilling and objectively analyze specific domain problems to choose the best tool for the job, setting personal developer preferences aside.
    • Counter Anti-Pattern Warning: Over-adopting bleeding-edge, unproven, or experimental technologies/libraries to stay on the absolute latest trend. This leads to code instability, reduced readability, and maintenance strain across software teams.