Creating an Abstract Method

Problem

You want to define a method of a class, but leave it for subclasses to fill in the actual implementations.

Solution

Define the method normally, but have it do nothing except raise a NotImplementedError:

	class Shape2D
	 def area
	 raise NotImplementedError.
	 new("#{self.class.name}#area is an 
abstract method.")
	 end
	end

	Shape2D.new.area
	# NotImplementedError: Shape2D#area is an 
abstract method.

A subclass can redefine the method with a concrete implementation:

	class Square < Shape2D
	 def initialize(length)
	 @length = length
	 end

	 def area
	 @length ** 2
	 end
	end

	Square.new(10).area 	 # => 100

 

Discussion

Ruby doesn't have a built-in notion of an abstract method or class, and though it has many built-in classes that might be considered "abstract," it doesn't enforce this abstractness the way C++ and Java do. For instance, you can instantiate an instance of Object or Numeric, even though those classes don't do anything by themselves.

In general, this is in the spirit of Ruby. But it's sometimes useful to define a superclass method that every subclass is expected to implement. The NotImplementedError error is the standard way of conveying that a method is not there, whether it's abstract or just an unimplemented stub.

Unlike other programming languages, Ruby will let you instantiate a class that defines an abstract method. You won't have any problems until you actually call the abstract method; even then, you can catch the NotImplementedError and recover. If you want, you can make an entire class abstract by making its initialize method raise a NotImplementedError. Then no one will be able to create instances of your class:[4]

[4] Of course, unless you freeze the class afterwards, someone else can reopen your class, define an empty initialize, and then create instances of your class.

	class Shape2D
	 def initialize
	 raise NotImplementedError.
	 new("#{self.class.name} is an abstract class.")
	 end
	end

	Shape2D.new
	# NotImplementedError: Shape2D is an abstract class.

We can do the same thing in less code by defining a decorator method of Class that creates an abstract method by the given name.

	class Class
	 def 
abstract(*args)
	 args.each do |method_name|
	
	 define_method(method_name) do |*args|
	 if method_name == :initialize
	 msg = "#{self.class.name} is an abstract class."
	 else
	 msg = "#{self.class.name}##{method_name} is an abstract method."
	 end
	 raise NotImplementedError.new(msg)

	 end
	 end
	 end
	end

Here's an abstract class that defines an abstract method move:

	class Animal
	 abstract :initialize, :move
	end

	Animal.new
	# NotImplementedError: Animal is an abstract class.

Here's a concrete subclass that doesn't bother to define an implementation for the abstract method:

	class Sponge < Animal
	 def initialize
	 @type = :Sponge
	 end
	end

	sponge = Sponge.new
	sponge.move
	# NotImplementedError: Sponge#move is an abstract method.

Here's a concrete subclass that implements the abstract method:

	class Cheetah < Animal
	 def initialize
	 @type = :Cheetah
	 end

	 def move
	 "Running!"
	 end
	end

	Cheetah.new.move
	# => "Running!"

Abstract methods declared in a class are, by convention, eventually defined in the subclasses of that class. But Ruby doesn't enforce this either. An abstract method has a definition; it just happens to be one that always throws an error.

Since Ruby lets you reopen classes and redefine methods later, the definition of a concrete method can happen later in time instead of further down the inheritance tree. The Sponge class defined above didn't have a move method, but we can add one now:

	class Sponge
	 def move
	 "Floating on ocean currents!"
	 end
	end
	sponge.move
	# => "Floating on ocean currents!"

You can create an abstract singleton method, but there's not much point unless you intend to fill it in later. Unlike instance methods, singleton methods aren't inherited by subclasses. If you were to define Superclass.foo abstract, then define it for real as Subclass.foo, you would have accomplished little: Superclass.foo would still exist separately and would still be abstract.


Strings

Numbers

Date and Time

Arrays

Hashes

Files and Directories

Code Blocks and Iteration

Objects and Classes8

Modules and Namespaces

Reflection and Metaprogramming

XML and HTML

Graphics and Other File Formats

Databases and Persistence

Internet Services

Web Development Ruby on Rails

Web Services and Distributed Programming

Testing, Debugging, Optimizing, and Documenting

Packaging and Distributing Software

Automating Tasks with Rake

Multitasking and Multithreading

User Interface

Extending Ruby with Other Languages

System Administration



Ruby Cookbook
Ruby Cookbook (Cookbooks (OReilly))
ISBN: 0596523696
EAN: 2147483647
Year: N/A
Pages: 399

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net