OOP Worksheet: Dog & Shape Classes
OOP Worksheet: Dog & Shape Classes
The Dog class uses encapsulation, one of the primary principles of object-oriented programming, to restrict access to the attributes 'name' and 'colour' by defining them as private. The class provides public procedures such as 'setColour', 'getColour', and 'getName' to modify or retrieve these attributes, thereby enforcing control over how these data members are accessed and modified. This design ensures that the attributes can only be accessed or changed through the specified methods, which provides flexibility as it allows the manipulation of dog attributes while maintaining data integrity .
To track the number of times each dog barks, you can add a new private attribute 'barkCount' to the Dog class. Modify the 'bark' procedure to increment 'barkCount' each time the procedure is called. Provide public methods 'getBarkCount' to retrieve the count value and 'resetBarkCount' if a reset is needed. This encapsulates the tracking mechanism within the Dog class, following encapsulation and ensuring that direct access to the 'barkCount' variable is restricted, thereby maintaining control over the associated data integrity .
Method overriding enables a subclass to provide a specific implementation of a method already defined in its superclass. In the Puppy class, overriding 'bark' allows for a new behavior ('Yap!') that distinguishes it from the Dog class's 'Woof!' sound. This promotes flexibility and specificity in behavior replication. However, challenges include ensuring consistent performance across subclasses and potential for increased complexity if not managed correctly, as changes in the superclass may require corresponding changes in all subclasses .
Encapsulation helps protect the integrity of data by limiting external access to the class attributes, permitting access only through well-defined interfaces. In classes like Dog and Puppy, encapsulation ensures that attributes like 'name', 'colour', and 'shoesChewed' are not directly accessed or modified by client code, which helps maintain data integrity and supports the class's internal logic without interference. It also enables the developer to change the internal implementation without affecting external code that interacts with the class .
Polymorphism is evident in how each subclass of the Shape class can redefine the method 'calculateArea' to fit its geometrical specifics: a Rectangle uses height and width, while a Circle would use radius to calculate its area. This ability allows different shapes to implement their logic while sharing a common interface, enhancing flexibility and scalability. Polymorphism allows for dynamic method invocation, enabling general code to work with objects of different types as long as they share the same interface, which facilitates code reuse and robustness .
In Task 4, inheritance is used by having Rectangle and Circle as subclasses of Shape, which means they inherit attributes like 'colourFill', 'colourOutline', and the method 'calculateArea'. This allows both subclasses to use and adapt shared functionality and enables code reusability, where common characteristics are managed in one place (Shape class). This design reduces redundancy, aligns with the DRY principle (Don't Repeat Yourself), and fosters scalability, as changes to common behavior such as 'calculateArea' only need to be made in one place while subclass-specific functionality can extend or customize inherited methods .
Effective strategies include unit testing each overridden method with various inputs to confirm expected behaviors. For Puppy, tests would involve creating instances and verifying that the 'bark' method outputs 'Yap!' as expected. Similarly, each Shape subclass should be instantiated and 'calculateArea' should be tested with valid dimensions to ensure correct area calculations. Mocking or stubbing can simulate superclass methods to ensure overridden functions operate correctly. Maintaining a comprehensive test suite for inherited functionality ensures robustness and early detection of regressions or errors due to modifications .
The Dog class's instantiation involves setting the 'name' and 'colour' attributes via its constructor. The Puppy class, however, extends this by calling the base class (Dog) constructor using 'super.new()' to handle inherited attributes ('name', 'colour'), and adds its own attribute 'dob' directly in its constructor. This approach allows the subclass to initialize its specific attribute while leveraging the existing logic for inherited attributes, showcasing how constructors are extended to accommodate subclass-specific needs without duplicating code .
Adding a new method to the Shape class would automatically make it available to its subclasses, such as Rectangle and Circle, unless they override it. This can enhance subclass capabilities by providing new shared functionality without modifying subclass code. However, it could also introduce dependencies if the method’s logic conflicts with or needs to integrate into the subclass-specific behavior, which would require careful assessment of its impact on existing subclass methods .
The Puppy class inherits from the Dog class, which means it gains all the methods and attributes of the Dog class unless it overrides them. Inheritance allows the Puppy class to use the procedures from the Dog class like 'bark', 'getName', and others unless explicitly redefined. The Puppy class redefines the 'bark' method to print 'Yap!' instead of 'Woof!', showcasing polymorphism, where a subclass modifies the behavior of a method defined in its superclass .