Object-Oriented Design Patterns Lab 2021
Object-Oriented Design Patterns Lab 2021
The business rule for cash withdrawal is implemented using the Chain of Responsibility pattern, where authorization handlers (Cashier, Senior Officer, Manager) are part of a linked chain, each checking if they can handle the given amount. The Cashier handles amounts up to Tk. 10,000; the Senior Officer handles amounts between Tk. 10,000 and Tk. 1,000,000 with Cashier's co-authorization; and the Manager oversees amounts over Tk. 1,000,000 with Senior Officer's co-authorization. This separation allows for flexible and clear authorization processes based on institutional roles, enabling future modifications without impacting existing logic structure .
The default initialization strategy for shape properties involves setting predefined values for each shape's dimensions. For instance, a Circle is initialized with a default radius of 1.0, while a Rectangle gets a width of 2.0 and a height of 1.5, and a Triangle is initialized with all sides set to 1.0. This approach ensures robustness by providing a valid state for any shape upon creation, avoiding errors linked to uninitialized variables, and allowing developers to verify behaviors before setting custom values .
Using default constructors offers the advantage of quickly creating objects with a stable initial state, which aids in testing and ensures operational robustness when specific parameters are not yet determined. In the context of the shapes example, it allows the creation of shape objects without needing immediate parameter values; however, it risks inadvertently obscuring bugs if default values lead to incorrect assumptions in calculations or tests. Balancing this requires clear documentation and potentially warning log outputs when default values are used .
To integrate a complex geometrical shape, you would first ensure the new shape class implements the `Shape` interface, defining the `getArea` method. Next, update `ShapeFactory`'s `createShape` method with a new case for the complex shape, passing any necessary parameters, or setting defaults. Extensions might include overloading with specific constructors or employing dependency injection for greater complexity in shape creation. This keeps with Factory Pattern principles by preserving single-responsibility and openness for extension .
The code examples adhere to naming conventions where class names are TitleCased (e.g., `ShapeFactory`), methods are camelCased (e.g., `createShape`), and constants are in UPPERCASE (e.g., unspecified). Following these conventions enhances readability and understanding, as developers can quickly identify the purpose and scope of identifiers by name alone. Consistent naming also facilitates code collaboration in teams and reduces the likelihood of bugs related to misinterpreting variable roles .
The Factory Pattern facilitates the creation of different shapes by encapsulating the instantiation logic within a single factory class, thereby promoting code reusability and separation of concerns. In the example, the `ShapeFactory` class uses a static method `createShape`, which takes a shape type as a String and optional parameters (such as radius for a circle, length for a rectangle, etc.). Depending on the shape type provided, the factory instantiates the appropriate shape object (Circle, Triangle, or Rectangle) with relevant constructor parameters, or defaults if none are provided .
The Triangle class defaults to side lengths of 1.0, creating an equilateral triangle. While simple, this choice might not be representative in practical applications needing diverse triangle types. Alternative default values closer to more common triangle proportions (e.g., a right triangle or scalene triangle) could enhance utility by better reflecting real-world scenarios, potentially leading to broader testing coverage and improved initial test outcomes .
The `Shape` interface defines a common contract through the `getArea` method, which all shape classes (Circle, Triangle, Rectangle) implement. This promotes polymorphism by allowing the use of shape objects interchangeably in the application. For instance, the `ShapeFactory` and `Main` classes can operate on any shape object through the `Shape` interface, regardless of the specific implementation. This is beneficial as it reduces code coupling, increases flexibility, and simplifies the introduction of new shapes without altering client code .
The Chain of Responsibility pattern effectively supports the dynamic validation of authorization rules by separating concerns among different roles (Cashier, Senior Officer, Manager) and allowing them to decide on handling requests based on their authority levels. Each handler in the chain either processes the request (if conditions are met) or passes it along to the next handler. However, to improve, adding a logging mechanism for each authorization step could provide transparency, and using a more flexible configuration system for setting authorization limits instead of hardcoded values could enhance maintainability and scalability .
The `createShape` method in `ShapeFactory` returns null if an invalid shape type is passed, which may lead to NullPointerExceptions when methods are called on shape objects. This lack of exception handling prevents immediate feedback of invalid input, potentially leading to runtime errors that are harder to trace. Implementing exception handling would allow explicit error messages and stop the execution flow when invalid data is encountered, improving the robustness and debugging process .