CSC/ECE 517 Fall 2010/ch7 7f LG: Difference between revisions

From Expertiza_Wiki
Jump to navigation Jump to search
No edit summary
Line 1: Line 1:
=Call “super” Anti-Pattern=
=Call “super” Anti-Pattern=
==Introduction==
==Introduction==
Having to call super when inheriting from a super-class is considered an anti-pattern or code smell. Whenever you have to remember to do something besides inheriting the method every time, chances are that you will forget it and this is a sign of a bad API. We can solve this by defining a public method that does the housekeeping and calls a second method, often called the hook, which the subclass has to override. Following this refactor will strengthen the API by alleviating the programmer from remembering to call super each time.
Having to call super when inheriting from a super-class is considered an anti-pattern or code smell. Whenever you have to remember to do something besides inheriting the method every time, chances are that you will forget it and this is a sign of a bad API. We can solve this by defining a public method that does the housekeeping and calls a second method, often called the 'hook', which the subclass has to override. Following this refactor will strengthen the API by alleviating the programmer from remembering to call super each time.
==Description==
It is not the need to perform an action on the super class, but the requirement of calling the parent what we considered an anti-pattern. An API following the call super anti-pattern relies on the user to make the call, hence malfunctioning whenever the user forgets to add the call to his/her subclassed method.
 
Adding a 'hook' is not always possible; the source code of the parent class might not be available in a closed/black-box API. This is not the only problem inheriting closed API classes; the user must keep an eye on the changes done in the class by the developer because it might break the subclass. A possible solution to the black-box class is languages’ annotation capabilities.
==Example==
==Example==
The following example is inspired in both the Wikipedia [[#References|[1]]] and Martin Fowler’s [[#References|[2]]] examples. By design, each subclass of vehicle should call super when overriding MoveForward and then perform their specific behavior. The programmer must remember to do so, if he does not, the vehicle will not move.
The following example is inspired in both the Wikipedia [[#References|[1]]] and Martin Fowler’s [[#References|[2]]] examples. By design, each subclass of vehicle should call super when overriding MoveForward and then perform their specific behavior. The programmer must remember to do so, if he does not, the vehicle will not move.
Line 24: Line 28:
}
}
</pre>
</pre>
Following the solution proposed in Martin Fowler’s article [[#References|[2]]], we refactor this code adding a hook called AfterMoveForward, which is the method the subclasses must override. Whenever we call MoveForward within a Car object, the appropriate movement and car-specific action will be performed.  
Following the solution proposed in Martin Fowler’s article [[#References|[2]]], we refactor this code adding a 'hook' called ''AfterMoveForward'', which is the method the subclasses must override. Whenever we call MoveForward within a Car object, the appropriate movement and car-specific action will be performed.  
<pre>
<pre>
public class Vehicle {
public class Vehicle {
Line 45: Line 49:
...
...
}
}
</pre>
===Real World Examples===
====Ruby on Rails====
Refactoring Rails 1.x tests to work with Rails 2.x might require the use of the call super anti-pattern. Michael Koziarski points out that functional tests inheriting ''ActiveSupport::TestCase'' that define a custom ''setup'' method must call ''super'' in Rails 2.x [[#References|[3]]]. A custom ''setup'' is one that does something else besides defining ''@controller'', ''@request'' and ''@response''.
<pre>
class xControllerTest < ActionController::TestCase
def setup
super
...
end
...
</pre>
</pre>


Line 51: Line 66:


[[#References|[2]]] Martin Fowler. (2010, December) Martin Fowler. [Online]. http://martinfowler.com/bliki/CallSuper.html
[[#References|[2]]] Martin Fowler. (2010, December) Martin Fowler. [Online]. http://martinfowler.com/bliki/CallSuper.html
[[#References|[3]]] Michael Koziarski. (2007, December) redemption in a blog. [Online]. http://blog.codefront.net/2007/12/27/dry-in-your-functional-and-actionmailer-tests-rails-20-a-feature-a-day-7/

Revision as of 04:33, 2 December 2010

Call “super” Anti-Pattern

Introduction

Having to call super when inheriting from a super-class is considered an anti-pattern or code smell. Whenever you have to remember to do something besides inheriting the method every time, chances are that you will forget it and this is a sign of a bad API. We can solve this by defining a public method that does the housekeeping and calls a second method, often called the 'hook', which the subclass has to override. Following this refactor will strengthen the API by alleviating the programmer from remembering to call super each time.

Description

It is not the need to perform an action on the super class, but the requirement of calling the parent what we considered an anti-pattern. An API following the call super anti-pattern relies on the user to make the call, hence malfunctioning whenever the user forgets to add the call to his/her subclassed method.

Adding a 'hook' is not always possible; the source code of the parent class might not be available in a closed/black-box API. This is not the only problem inheriting closed API classes; the user must keep an eye on the changes done in the class by the developer because it might break the subclass. A possible solution to the black-box class is languages’ annotation capabilities.

Example

The following example is inspired in both the Wikipedia [1] and Martin Fowler’s [2] examples. By design, each subclass of vehicle should call super when overriding MoveForward and then perform their specific behavior. The programmer must remember to do so, if he does not, the vehicle will not move.

public class Vehicle {
	...
	public void MoveForward() {
		// Housekeeping
		ValidateMove();
		UpdateDisplay(POSITION);
		...
	}
	...
}
public class Car : Vehicle {
	...
	public void MoveForward() {
		base.MoveForward();
		UpdateDisplay(WHEEL_MARKS); // Do Car-specific actions
	}
	...
}

Following the solution proposed in Martin Fowler’s article [2], we refactor this code adding a 'hook' called AfterMoveForward, which is the method the subclasses must override. Whenever we call MoveForward within a Car object, the appropriate movement and car-specific action will be performed.

public class Vehicle {
	...
	public void MoveForward() {
		// Housekeeping
		ValidateMove();
		UpdateDisplay(POSITION);
		...
		AfterMoveForward();
	}
	protected abstract void AfterMoveForward();
	...
}
public class Car : Vehicle {
	...
	public void AfterMoveForward() {
		UpdateDisplay(WHEEL_MARKS); // Do Car-specific actions
	}
	...
}

Real World Examples

Ruby on Rails

Refactoring Rails 1.x tests to work with Rails 2.x might require the use of the call super anti-pattern. Michael Koziarski points out that functional tests inheriting ActiveSupport::TestCase that define a custom setup method must call super in Rails 2.x [3]. A custom setup is one that does something else besides defining @controller, @request and @response.

class xControllerTest < ActionController::TestCase
	def setup
		super
		...
	end
	...

References

[1] Wikipedia. (2010, December) Wikipedia - Call super. [Online]. http://en.wikipedia.org/wiki/Call_super

[2] Martin Fowler. (2010, December) Martin Fowler. [Online]. http://martinfowler.com/bliki/CallSuper.html

[3] Michael Koziarski. (2007, December) redemption in a blog. [Online]. http://blog.codefront.net/2007/12/27/dry-in-your-functional-and-actionmailer-tests-rails-20-a-feature-a-day-7/