Final Exam - Factory Method Design Pattern PDF

Summary

This document is a sample of a final exam focused on Factory Method Design Pattern. It outlines the structure, advantages and disadvantages, and when to utilize this pattern.

Full Transcript

Final Exam Factory Method Design Pattern What is the Factory Method Design Pattern? Factory Method Design Pattern is a creational design pattern used in software development. It provides an interface for creating objects in a superclass while...

Final Exam Factory Method Design Pattern What is the Factory Method Design Pattern? Factory Method Design Pattern is a creational design pattern used in software development. It provides an interface for creating objects in a superclass while allowing subclasses to specify the types of objects they create. This pattern simplifies object creation by using a dedicated method, reducing dependencies. It allows subclasses to create specific objects, improving flexibility and maintainability. When to Use the Factory Method Design Pattern The Factory Method Pattern simplifies object creation by using an interface or abstract class, hiding implementation details and reducing dependencies. It provides flexibility to handle different product versions or new types by defining specific factory methods for each. If your object creation process is complex or varies under different conditions, using a factory method can make your client code simpler and promote reusability. Factories can also encapsulate configuration logic, allowing clients to customize the object creation process by providing parameters or options to the factory method. Components of Factory Method DFeisgn Pattern Creator Concrete Creator Product Final Exam 1 Concrete Product Advantages of the Factory Method Separates creation logic from client code, improving flexibility. New product types can be added easily. Simplifies unit testing by allowing mock product creation. Centralizes object creation logic across the application. Hides specific product classes from clients, reducing dependency Disadvantages of the Factory Method Adds more classes and interfaces, which can complicate maintenance. Slight performance impacts due to polymorphism. Concrete creators are linked to their products. Clients need knowledge of specific subclasses. May lead to unnecessary complexity if applied too broadly. Factory logic can be harder to test. Abstract Factory Pattern What is the Abstract Factory Pattern The Abstract Factory Pattern is a way of organizing how you create groups of things that are related to each other. It provides a set of rules or instructions that let you create different types of things without knowing exactly what those things are. This helps you keep everything organized and lets you switch between different types easily. Components of Abstract Factory Pattern Final Exam 2 When to use Abstract Factory Pattern When your system requires multiple families of related products and you want to ensure compatibility between them. When you need flexibility and extensibility, allowing for new product variants to be added without changing existing client code. When you want to encapsulate the creation logic, making it easier to modify or extend the object creation process without affecting the client. When you aim to maintain consistency across different product families, ensuring a uniform interface for the products. When not to use Abstract Factory Pattern When product families are unlikely to change, as implementing this pattern may add unnecessary complexity. Final Exam 3 When your application only needs single, independent objects rather than families of related products. When the overhead of maintaining multiple factories outweighs the benefits, especially in smaller applications. When simpler patterns like Factory Method or Builder can meet your needs without the added complexity of Abstract Factory. Flyweight Design Pattern What is a Flyweight Design Pattern? The Flyweight Design Pattern is a way to save memory in applications that create a large number of similar objects. Instead of creating a new object for each instance, the Flyweight pattern reuses existing ones wherever possible, sharing common parts between objects. Shared vs. Unique Data: Objects are split into shared (intrinsic) data and unique (extrinsic) data. The shared data is stored in a central place and reused, while the unique data is kept separately. Example: Imagine a text editor displaying thousands of characters. Using Flyweight, the program can store each unique character style (like font or color) only once and reuse it, rather than duplicating it for every character When to use Flyweight Design Pattern When you need to create a large number of similar objects: For instance, suppose you have to make hundreds of graphical objects (such as buttons and icons) with similar attributes (such as color and picture) but different positions and other variable features. When memory consumption is a concern: By sharing common state, the Flyweight pattern can assist reduce the memory footprint of memory-intensive Final Exam 4 applications that would otherwise require a large amount of RAM to create several instances of comparable objects. When performance optimization is needed: The pattern can enhance efficiency by lowering the overhead related to trash collection, object generation, and destruction by minimizing the number of objects. When not to use Flyweight Design Pattern When objects have unique intrinsic states: If each object instance requires a unique set of intrinsic state that cannot be shared with other objects, applying the Flyweight pattern may not provide significant benefits. When the overhead of implementing the pattern outweighs the benefits: If the number of objects is relatively small or the shared state is minimal, the complexity introduced by the Flyweight pattern may not justify its implementation. When mutable objects are involved: The Flyweight pattern is best suited for immutable objects or objects whose intrinsic state does not change once initialized. If objects frequently change their intrinsic state, managing shared state can become complex and error-prone. When the application does not have performance or memory constraints: If memory usage and performance are not critical concerns for your application, implementing the Flyweight pattern may add unnecessary complexity without significant benefits. What is the Memento Design Pattern? The Memento design pattern is a behavioral pattern that is used to capture and restore an object’s internal state without violating encapsulation. It allows you to save and restore the state of an object to a previous state, providing the ability to undo or roll back changes made to the object. Final Exam 5 As your application progresses, you may want to save checkpoints in your application and restore them to those checkpoints later. The intent of the Memento Design pattern is without violating encapsulation, to capture and externalize an object’s internal state so that the object can be restored to this state later. Benefits of Memento Pattern : Encapsulation: The Memento pattern allows you to encapsulate the state of the document within Memento objects, preventing direct access and manipulation of the document’s state. Undo Functionality: By storing snapshots of the document’s state at different points in time, the Memento pattern enables the implementation of an undo feature, allowing users to revert changes and restore previous document states. Separation of Concerns: The Memento pattern separates the responsibility of state management from the document itself, promoting cleaner and more maintainable code. When to use Memento Design Pattern Undo functionality: When you need to implement an undo feature in your application that allows users to revert changes made to an object’s state. Snapshotting: When you need to save the state of an object at various points in time to support features like versioning or checkpoints. Transaction rollback: When you need to rollback changes to an object’s state in case of errors or exceptions, such as in database transactions. Caching: When you want to cache the state of an object to improve performance or reduce redundant computations. When not to use Memento Design Pattern Final Exam 6 Large object state: If the object’s state is large or complex, storing and managing multiple snapshots of its state can consume a significant amount of memory and processing resources. Frequent state changes: If the object’s state changes frequently and unpredictably, storing and managing snapshots of its state may become impractical or inefficient. Immutable objects: If the object’s state is immutable or easily reconstructible, there may be little benefit in using the Memento pattern to capture and restore its state. Overhead: Introducing the Memento pattern can add complexity to the codebase, especially if the application does not require features like undo functionality or state rollback. Adapter Design Pattern What is Adapter Design Pattern? Two incompatible interfaces or systems can cooperate by using the adapter design pattern, a structural design pattern. Because of incompatible interfaces, it serves as a bridge between two classes that would not otherwise be able to communicate. The adapter approach is very helpful when attempting to incorporate third-party libraries or legacy code into a new system. 1. Class Adapter (Inheritance-based) In this approach, the adapter class inherits from both the target interface (the one the client expects) and the adaptee (the existing class needing adaptation). Programming languages that allow multiple inheritance, like C++, are more likely to use this technique. However, in languages like Java and C#, which do not support multiple inheritance, this approach is less frequently used. Final Exam 7 2. Object Adapter (Composition-based) The object adapter employs composition instead of inheritance. In this implementation, the adapter holds an instance of the adaptee and implements the target interface. This approach is more flexible as it allows a single adapter to work with multiple adaptees and does not require the complexities of inheritance. The object adapter is widely used in languages like Java and C#. 3. Two-way Adapter A two-way adapter can function as both a target and an adaptee, depending on which interface is being invoked. This type of adapter is particularly useful when two systems need to work together and require mutual adaptation. 4. Interface Adapter (Default Adapter) When only a few methods from an interface are necessary, an interface adapter can be employed. This is especially useful in cases where the interface contains many methods, and the adapter provides default implementations for those that are not needed. This approach is often seen in languages like Java, where abstract classes or default method implementations in interfaces simplify the implementation process. Pros of Adapter Design Pattern: Reuses existing code without modifications, promoting cleaner architecture. Separates interface adaptation from core logic, keeping classes focused on their primary duties. Allows switching between different interfaces by changing adapters without altering the system. Final Exam 8 Simplifies swapping or modifying components without affecting other parts of the system. Cons of Adapter Design Pattern: Adds complexity and can make the code harder to understand. May introduce slight performance overhead, especially with complex transformations. Managing multiple adapters for various interfaces can become challenging. Overusing adapters for minor changes can lead to unnecessary complexity. Requires multiple adapters to handle multiple interfaces, which can complicate the design. When to Use Adapter Design Pattern: To connect incompatible systems or components. To integrate legacy code or libraries into the current system. To add new components to a growing project without impacting existing code. To centralize compatibility changes, making maintenance easier and reducing bugs. When Not to Use Adapter Design Pattern: When all components are compatible, and the system is simple. In performance-critical environments, where overhead is a concern. When interface compatibility isn’t an issue. For short-term projects where the benefits of an adapter don't outweigh the effort. Observer Patttern What is the Observer Design Pattern? Final Exam 9 The Observer Design Pattern is a behavioral design pattern that defines a one-to- many dependency between objects. When one object (the subject) changes state, all its dependents (observers) are notified and updated automatically. It primarily deals with the interaction and communication between objects, specifically focusing on how objects behave in response to changes in the state of other objects. Below are some key points about observer design pattern: Defines how a group of objects (observers) interact based on changes in the state of a subject. Observers react to changes in the subject’s state. The subject doesn’t need to know the specific classes of its observers, allowing for flexibility. Observers can be easily added or removed without affecting the subject. When to Use the Observer Design Pattern: When one object needs to notify multiple others about changes. To keep objects loosely connected, avoiding dependency on each other’s details. When observers should automatically respond to changes in the subject’s state. To easily add or remove observers without modifying the subject. For event systems where components need to react without direct connections. When Not to Use the Observer Design Pattern: When object relationships are simple and don’t require notifications. In performance-critical systems, as too many observers can cause overhead. When subject and observers are tightly coupled, undermining decoupling benefits. Final Exam 10 When the number of observers is fixed and won’t change. When the order of notifications is critical, as it may be unpredictable. Decorator Design Pattern The Decorator Design Pattern is a structural design pattern that enables you to dynamically add new behaviors or functionality to individual objects without altering the structure or behavior of other objects in the same class. It is particularly useful for achieving flexibility and adhering to the Open-Closed Principle—allowing extensions without modifying existing code. Key Benefits: Enhances functionality in a modular and reusable way. Avoids subclassing by composing behaviors dynamically at runtime. Keeps the codebase clean and maintainable. This pattern is commonly used in scenarios like adding visual effects in UI components or extending the behavior of objects in a layered manner. Characteristics of the Decorator Design Pattern: Promotes flexibility and extensibility: Enables developers to dynamically combine different functionalities at runtime without altering the core object. Adheres to the Open/Closed Principle: Allows adding new decorators without modifying existing classes, ensuring a modular and maintainable codebase. Supports optional features: Ideal for scenarios where optional behaviors or features need to be layered on objects, making it reusable and adaptable. Common use cases: Frequently used in text formatting (e.g., bold, italic, underline), graphical user interfaces (e.g., adding scrollbars, borders), and product customization (e.g., adding flavors to coffee or toppings to ice cream). Advantages of the Decorator Design Pattern: Final Exam 11 1. Adheres to the Open-Closed Principle: New functionality can be added to existing classes without modifying their source code. 2. Flexibility: Responsibilities or behaviors can be added or removed from objects dynamically at runtime. 3. Reusable Code: Decorators are modular components that can be applied across different objects, reducing code duplication. 4. Composition over Inheritance: Avoids rigid and deep inheritance hierarchies by composing objects with decorators. This reduces tight coupling. 5. Dynamic Behavior Modification: Provides the ability to adapt an object’s behavior based on changing requirements or user preferences. 6. Clear Code Structure: Promotes a structured design, making it easier to understand how behaviors are added to objects. Disadvantages of the Decorator Design Pattern: 1. Complexity: Adding multiple decorators can lead to complex, nested structures that are harder to read, debug, and maintain. 2. Increased Number of Classes: Often leads to many small, specialized decorator classes, increasing the maintenance burden. 3. Order of Decoration: The sequence in which decorators are applied can impact behavior, making management tricky in complex scenarios. Final Exam 12 4. Risk of Overuse: Overusing decorators can make the code unnecessarily complicated. They should be applied judiciously. 5. Language Constraints: In some programming languages, implementing decorators can be verbose and less intuitive due to limited language support. Facade Method Design Pattern What is the Facade Method Design Pattern? Facade Method Design Pattern provides a unified interface to a set of interfaces in a subsystem. Facade defines a high-level interface that makes the subsystem easier to use. Final Exam 13 In the above diagram, Structuring a system into subsystems helps reduce complexity. A common design goal is to minimize the communication and dependencies between subsystems. One way to achieve this goal is to introduce a Facade object that provides a single simplified interface to the more general facilities of a subsystem. When to use Facade Method Design Pattern A Facade provide a simple default view of the subsystem that is good enough for most clients. There are many dependencies between clients and the implementation classes of an abstraction. A Facade to decouple the subsystem from clients and other subsystems, thereby promoting subsystem independence and portability. Facade define an entry point to each subsystem level. If subsystem are dependent, then you can simplify the dependencies between them by making them communicate with each other through their facades. Advantages of the Facade Design Pattern: 1. Simplified Interface: Provides a clear and concise interface, making it easier for clients to interact with a complex system. Hides internal system details, reducing the cognitive load for clients. 2. Reduced Coupling: Disconnects clients from the internal workings of the subsystem, reducing reliance on its implementation. Final Exam 14 Promotes reusability and modularity of system components. Allows for independent testing and development of subsystems. 3. Encapsulation: Encapsulates complex subsystem interactions, shielding clients from implementation changes. Enables subsystem modifications without impacting clients as long as the facade interface remains stable. 4. Improved Maintainability: Makes it easier to modify or extend the system without affecting clients. Supports refactoring or optimization of subsystems while keeping client code intact. Disadvantages of the Facade Design Pattern: 1. Increased Complexity: Adds an extra layer of abstraction, which may make the code harder to understand and debug. 2. Reduced Flexibility: Acts as a single access point, which can restrict clients from accessing specific functionalities within the subsystem. 3. Overengineering: Using the facade pattern for simple systems can add unnecessary complexity. Requires careful evaluation to avoid overusing facades in inappropriate situations. 4. Potential Performance Overhead: The additional indirection of the facade can introduce slight performance costs, especially in performance-critical applications. Conclusion: Final Exam 15 The Facade Pattern is ideal for simplifying access to complex systems or creating a unified external communication layer. It focuses on interfaces rather than implementation, making systems easier to use and maintain. However, it’s important to balance the benefits of abstraction against the potential for added complexity and performance trade-offs. Proxy Design Pattern Proxy is a structural design pattern that lets you provide a substitute or placeholder for another object. A proxy controls access to the original object, allowing you to perform something either before or after the request gets through to the original object. Steps to Implement the Proxy Design Pattern: 1. Create a Service Interface (if not already available): Define a common interface for the real service and the proxy to ensure they are interchangeable. If extracting an interface is impractical, make the proxy a subclass of the service class to inherit its interface. 2. Create the Proxy Class: Add a field in the proxy class to hold a reference to the service object. Decide how the service object will be managed: Proxy-managed lifecycle: The proxy creates and manages the service object. Client-provided service: The client passes the service object to the proxy via its constructor. 3. Implement Proxy Methods: Implement the methods in the proxy class to fulfill their specific purposes. Typically, the proxy performs additional tasks (e.g., logging, access control, caching) before or after delegating the actual work to the service Final Exam 16 object. 4. Introduce a Creation Mechanism: Provide a mechanism to decide whether the client receives a proxy or the actual service object. This could be: A static method in the proxy class. A factory method that encapsulates the creation logic. 5. Add Lazy Initialization (Optional): Implement lazy initialization to delay the creation of the service object until it is actually needed. This can improve performance and resource usage if the service is expensive to create or may not always be needed. Example Workflow: 1. Define an interface ServiceInterface. 2. Create the RealService class that implements ServiceInterface. 3. Implement the ProxyService class that also implements ServiceInterface. Add a reference to RealService and delegate work to it after adding any additional behavior. 4. Provide a static or factory method to decide whether to return a proxy or the real service. 5. Optionally, implement lazy initialization in ProxyService to instantiate RealService only when required. By following these steps, the proxy can seamlessly intercept and manage interactions between the client and the real service. Pros and Cons You can control the service object without clients knowing about it. Final Exam 17 You can manage the lifecycle of the service object when clients don’t care about it. The proxy works even if the service object isn’t ready or is not available. Open/Closed Principle. You can introduce new proxies without changing the service or clients. Cons The code may become more complicated since you need to introduce a lot of new classes. The response from the service might get delayed. Relations with Other Patterns With Adapter you access an existing object via different interface. With Proxy, the interface stays the same. With Decorator you access the object via an enhanced interface. Facade is similar to Proxy in that both buffer a complex entity and initialize it on its own. Unlike Facade, Proxy has the same interface as its service object, which makes them interchangeable. Decorator and Proxy have similar structures, but very different intents. Both patterns are built on the composition principle, where one object is supposed to delegate some of the work to another. The difference is that a Proxy usually manages the life cycle of its service object on its own, whereas the composition of Decorators is always controlled by the client. Composite Design Pattern Composite is a structural design pattern that lets you compose objects into tree structures and then work with these structures as if they were individual objects. Applicability Final Exam 18 Use the Composite pattern when you have to implement a tree-like object structure. The Composite pattern provides you with two basic element types that share a common interface: simple leaves and complex containers. A container can be composed of both leaves and other containers. This lets you construct a nested recursive object structure that resembles a tree. Use the pattern when you want the client code to treat both simple and complex elements uniformly. All elements defined by the Composite pattern share a common interface. Using this interface, the client doesn’t have to worry about the concrete class of the objects it works with. Steps to Implement the Composite Design Pattern (Simplified): 1. Model as a Tree Structure: Break down the app into simple elements (leaves) and containers. Containers must hold both simple elements and other containers. 2. Create a Component Interface: Define methods that make sense for both simple and complex elements, like operation(). 3. Create Leaf Classes: Implement the component interface for simple, indivisible elements (e.g., a Leaf class). 4. Create a Composite (Container) Class: Implement the component interface for complex elements. Use a list to store both leaves and other containers. 5. Delegate Work in Containers: The container delegates the work to its sub-elements (e.g., calling operation() on each child). 6. Add Methods for Child Management: Final Exam 19 In the container, provide methods like add() and remove() to manage child elements. 7. Consider Interface Segregation: If child-management methods are in the component interface, they won’t be used in the leaf class, which violates the Interface Segregation Principle. However, it allows treating all elements uniformly. Pros and Cons You can work with complex tree structures more conveniently: use polymorphism and recursion to your advantage. Open/Closed Principle. You can introduce new element types into the app without breaking the existing code, which now works with the object tree. Cons It might be difficult to provide a common interface for classes whose functionality differs too much. In certain scenarios, you’d need to overgeneralize the component interface, making it harder to comprehend. Relations with Other Patterns (Simplified): 1. Builder: Use the Builder pattern to create complex Composite trees, especially when the construction steps need to be recursive. 2. Chain of Responsibility: Chain of Responsibility can be used with Composite. When a leaf gets a request, it may pass it up the tree through parent components until it reaches the root. 3. Iterator: Use Iterator to traverse through a Composite tree and access its elements. 4. Visitor: Final Exam 20 The Visitor pattern allows you to perform operations on all elements in a Composite tree without modifying their classes. 5. Flyweight: Flyweight can be used for shared leaf nodes in a Composite tree to save memory by reusing objects. 6. Decorator: Composite and Decorator share a similar structure, using recursive composition. A Decorator adds additional responsibilities to a single component, while a Composite groups multiple children. They can work together: use Decorator to extend the behavior of a specific object in a Composite tree. 7. Prototype: When dealing with complex Composite and Decorator designs, the Prototype pattern helps by cloning existing structures instead of rebuilding them from scratch. State design pattern State is a behavioral design pattern that lets an object alter its behavior when its internal state changes. It appears as if the object changed its class. Applicability of the State Pattern (Simplified): 1. Different Behavior Based on State: Use the State pattern when an object behaves differently depending on its state, and the number of states is large or frequently changes. It allows you to separate state-specific logic into distinct classes, making the code easier to maintain and extend. 2. Reducing Complex Conditionals: Final Exam 21 Use the State pattern when a class has complex conditionals that change its behavior based on certain field values. It lets you move these conditionals into dedicated state classes, simplifying the main class and removing unnecessary fields. 3. Duplicate Code Across States: If your code has a lot of duplicate logic for similar states and transitions (like in a state machine), the State pattern can help by organizing common behavior in base classes, reducing code repetition. Pros and Cons Single Responsibility Principle. Organize the code related to particular states into separate classes. Open/Closed Principle. Introduce new states without changing existing state classes or the context. Simplify the code of the context by eliminating bulky state machine conditionals. Cons Applying the pattern can be overkill if a state machine has only a few states or rarely changes. Relations with Other Patterns Bridge, State, Strategy (and to some degree Adapter) have very similar structures. Indeed, all of these patterns are based on composition, which is delegating work to other objects. However, they all solve different problems. A pattern isn’t just a recipe for structuring your code in a specific way. It can also communicate to other developers the problem the pattern solves. State can be considered as an extension of Strategy. Both patterns are based on composition: they change the behavior of the context by delegating some work to helper objects. Strategy makes these objects completely independent Final Exam 22 and unaware of each other. However, State doesn’t restrict dependencies between concrete states, letting them alter the state of the context at will Singleton Singleton is a creational design pattern that lets you ensure that a class has only one instance, while providing a global access point to this instance. Pros and Cons : Pros You can be sure that a class has only a single instance. You gain a global access point to that instance. The singleton object is initialized only when it’s requested for the first time. Cons Violates the Single Responsibility Principle. The pattern solves two problems at the time. The Singleton pattern can mask bad design, for instance, when the components of the program know too much about each other. The pattern requires special treatment in a multithreaded environment so that multiple threads won’t create a singleton object several times. It may be difficult to unit test the client code of the Singleton because many test frameworks rely on inheritance when producing mock objects. Since the constructor of the singleton class is private and overriding static methods is impossible in most languages, you will need to think of a creative way to mock the singleton. Or just don’t write the tests. Or don’t use the Singleton pattern. Bridge Design Pattern Final Exam 23 The Bridge design pattern allows you to separate the abstraction from the implementation. It is a structural design pattern. There are 2 parts in Bridge design pattern : 1. Abstraction : the GUI layer of the app. 2. Implementation : the operating systems’ APIs. Final Exam 24 The bridge pattern allows the Abstraction and the Implementation to be developed independently and the client code can access only the Abstraction part without being concerned about the Implementation part. The abstraction is an interface or abstract class and the implementer is also an interface or abstract class. The abstraction contains a reference to the implementer. Children of the abstraction are referred to as refined abstractions, and children of the implementer are concrete implementers. Since we can change the reference to the implementer in the abstraction, we are able to change the abstraction’s implementer at run-time. Changes to the implementer do not affect client code. It increases the loose coupling between class abstraction and it’s implementation. Final Exam 25

Use Quizgecko on...
Browser
Browser