CSC/ECE 517 Fall 2010/ch7 7g mr

From Expertiza_Wiki
Revision as of 03:25, 30 November 2010 by Mwroda (talk | contribs)
Jump to navigation Jump to search

Square-Rectangle Anti-Pattern

The Square-Rectangle Anti-Pattern is a software development problem that can occur in Object Oriented Programming when using inheritance.

Description

Consider classes Rectangle and Square. One might reasonably define the Square class as a specific type of the Rectangle class since it passes the 'is a' test (Square is a Rectangle). This leads to several problems however, since subclasses are supposed to support all the methods of their super class.

Examples

Constructor

Conceptually this problem happens when the base class contains more information than the derived class. In the example of the Square and the Rectangle, both have two sides x and y, but in the case of a Rectangle the sides may be of different length and therefore two values must be maintained.

Within the code the base class Rectangle may have a constructor that accepts two parameters, x and y, to set the length of its sides. How is the Square class to implement this constructor? It could override the constructor but if the values passed for x and y are not the same, it would be unable to construct an instance of Square that satisfied the input criteria.

Mutating Methods

The situation is likewise for mutating methods, which modif an object which was already instantiated. For example the Rectangle class may have a method setX which modifies the size of only one side of the rectangle. When called on a Square, it would cause the object to no longer be a square.

This becomes particularly problematic when new methods are added to an existing base class after it has been subclassed. If it has been subclassed improperly, the subclasses may be broken by the new functionality in the base class.

Alternatives

Reverse the inheritance

Instead of having Square be a subclass of Rectangle, one could make Rectangle a subclass of Square. Afterall, you could say a Rectangle is a specialization of Square in which the sides don't need to be of the same length. Thus Square would define a constructor that takes only argument x. Rectangle could support that constructor and also provide another constructor which takes x and y. Likewise, Square could have only a setx method whereas Rectangle could also define a sety method.

This is somewhat of a contrived example since Square does not have any additional information than Rectangle. Such may not be the case in more realistic scenarios.

Absent constructor

The derived Rectangle class could just choose not to implement the constructor that takes arguments x and y. This would prevent any caller from instantiating an instance of Square with arguments x and y. This is confusing though, since one normally expects a subclass to support all the constructors of the base class.

Throw exception

The Square class could override any methods or constructors which violate the contract and throw an exception. In the constructor example above, it could throw an exception if the lengths provided for x and y are not equal. In the setx method example, it could check the value of y and throw an exception if the passed in value for x wasn't equal to y. This is somewhat dubious but does allow the setx method to be called if the value isn't changing.

In Java the UnsupportedOperationException could be thrown, which is a RuntimeException and therefore does not need to be declared. If another type of exception was thrown, it would have to be declared.

Return new value

In this solution, the super class method would need to return its new value X when a method is called to change it. The derived class reacts by stubbornly just returning its current value, thus forcing the caller to use a method supported for that class. This solution avoids throwing exceptions but could have undesireable consequences if the calling code is not careful to check the return value to make sure it was modified. But perhaps worse, it requires the method signatures to be changed in the base class to support the problem in the derived class.

Make method more powerful

The implementation for the setx method in Square could just change the value of y also. This results in the method becoming more powerful, and might not be what the caller intended.

Return instance of new class

If the constructor is called with different values for x and y, or if the setx method is called, the implementation on Square could instantiate an instance of Rectangle and return that instead. This requires the methods to declare a return type of the base class. This could have unexpected consequences on the program.

Make all classes immutable

In this approach, the classes are immutable and therefore the methods cannot change the existing class, they can only return new instances of the proper class. As in some of the previous solutions, this has the disadvantage of requiring significant modifications to the design of the base class. It also requires the calling code to perform more assignments and may result in more memory being consumed by all the immutable instances around.

See also

References

External Links