|
|
Line 1: |
Line 1: |
| ==Subclassing== | | ===Subclassing=== |
|
| |
|
| Subclassing is the concept of creating a specialization(subclass/ derived class) of a base class(superclass/ parent class) by inheriting the methods and instance data from the base class. | | Subclassing is a principle of creating a specialization(subclass/ derived class) of a base class(superclass/ parent class) by inheriting the methods and |
| | |
| | instance data from the baseclass. |
|
| |
|
| ==Why do we Subclass?== | | ==Why do we Subclass?== |
| 1. Code reuse
| | Code reuse |
| 2. Specialization: A subclass can define new methods it's superclass does not handle.
| | Specialization: A subclass can define new methods it's superclass does not handle. |
| 3. Method Overriding: An overridding method can either have minor modifications or be completely changed from its parent class'
| | Method Overriding: An overridding method can either have minor modifications or be completely changed from its parent class' implementation. |
| implementation. | |
|
| |
|
| ==Is Subclassing same as Subtyping?== | | ===Is Subclassing same as Subtyping?=== |
| | | ==Subtyping== |
| ===Subtyping===
| |
| A is said to be a type of B if A's specification is same as B's. Subtypes should satisfy the Liskov Substitution Principle which states, | | A is said to be a type of B if A's specification is same as B's. Subtypes should satisfy the Liskov Substitution Principle which states, |
|
| |
|
| If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of
| | If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is |
| P is unchanged when o1 is substituted for o2, then S is a subtype of T.
| |
|
| |
|
| | substituted for o2, then S is a subtype of T. |
|
| |
|
| /* Include Example here */
| | In most programming languages,for example java, ruby, C++, subclassing does not essentially mean subtyping. For a class to be a subtype, the subclass must |
|
| |
|
| | follow the Liskov Substitution Principle. However it is easy to override a method with a completely new implementation and fewer constraints. This is not |
|
| |
|
| In most programming languages,for example java, ruby, C++, subclassing does not essentially mean subtyping. For a class to be a subtype, the subclass must follow the Liskov Substitution Principle. However it is easy to override a method with a completely new implementation and fewer constraints. This is not checked by the compiler and we can create subclasses which are not subtypes.
| | checked by the compiler and we can create subclasses which are not subtypes. This is considered a bad approach, one of the reasons being, an argument to a |
|
| |
|
| ==Subclassing==
| | method may be declared of one class A, but the method may be called with an argument of some subclass B. If we do not know if B is a true subtype of A, then |
| Declaring one class to be a subclass of another class there by allowing a
| |
|
| |
|
| subclass to inherit the functionality from its super class is called
| | we cannot assume that the behavior guaranteed by A is actually guaranteed by B, and so we cannot reason locally about this method[link mit reference]. |
|
| |
|
| subclassing.
| | Subclass not Subtype |
|
| |
|
| /* we can include here the 4 perspectives of inheritance and then mention
| | class A { |
| | | int x; |
| LP principle , principle of least astonishment and
| | int get_x() |
| some other solid principles of inheritance like the The Open-Closed
| | { |
| | | return x; |
| Principle and The Single Responsibility Principle.
| | } |
| */ | | int sum(A a) { return x + a.x } |
| Links for the solid principles...
| | } |
| http://www.objectmentor.com/resources/articles/ocp.pdf
| |
| http://www.objectmentor.com/resources/articles/srp.pdf
| |
| | |
| ===Subclassing in Java===
| |
| | |
| ====Extends Keyword====
| |
| The extends is a Java keyword, which is used in inheritance process of
| |
| | |
| Java. It specifies the superclass in a class declaration using extends
| |
| | |
| keyword.To inherit a class, you simply incorporate the definition of one
| |
| | |
| class into another by using the extends keyword.
| |
| | |
| <pre>
| |
| public class A
| |
| { | |
| int a;
| |
| char b;
| |
| void method1()
| |
| {
| |
| }
| |
| } | |
| class B extends A //class B inherits the properties of class A
| |
| {
| |
| void method2()
| |
| {
| |
| }
| |
| } | |
| </pre>
| |
|
| |
| | |
| ===Abstract classes===
| |
| | |
| Abstract classes in Java are used to declare common characteristics of
| |
| | |
| subclasses. An abstract class cannot be instantiated i.e an object of an
| |
| | |
| abstract class cannot be created. It can only be used as a superclass for
| |
| | |
| other classes that extend the abstract class. Abstract classes are declared
| |
| | |
| using the abstract keyword. Abstract classes are used to provide a template
| |
| | |
| or design for concrete subclasses down the inheritance tree.
| |
| | |
| An abstract class can contain fields that describe the characteristics and
| |
| | |
| methods that describe the actions that a class can perform. An abstract
| |
| | |
| class can include methods that contain no implementation. These are called
| |
| | |
| abstract methods.If a class has any abstract methods, whether declared or
| |
| | |
| inherited, the entire class must be declared abstract. Abstract methods are
| |
| | |
| used to provide a template for the classes that inherit the abstract
| |
| | |
| methods.
| |
| | |
| Abstract classes cannot be instantiated; they must be subclassed, and
| |
|
| |
|
| actual implementations must be provided for the abstract methods. Any
| | class B { |
| | int y; |
|
| |
|
| implementation specified can, of course, be overridden by additional sub-
| | int get_y() |
| | { |
| | return y; |
| | } |
|
| |
|
| classes. An object must have an implementation for all of its methods. You
| | int sum(B b) |
| | | { |
| need to create a subclass that provides an implementation for the abstract
| | if(y > 0) |
| | | return x + b.x + y + b.y; |
| method.
| | else |
| | | return 0; |
| <pre>
| | } |
| abstract class A
| |
| { | |
| void method1()
| |
| {
| |
| ...
| |
| ...
| |
| ... | |
| } | | } |
| abstract void method2();
| |
| }
| |
| class B extends A
| |
| {
| |
| void method2()
| |
| {
| |
| ...
| |
| ...
| |
| }
| |
|
| |
| }
| |
| </pre>
| |
|
| |
| If the class B does not implement the method "method2" then even class B
| |
|
| |
| has to be declared as an Abstract class.
| |
|
| |
| Rules that should be followed before subclassing
| |
|
| |
| ===Subclassing vs Subtyping===
| |
|
| |
|
| |
|
| |
|
| |
| ===The Liskov Substitution Principle in class typed languages===
| |
| The Liskov Substitution Principle is a way of ensuring that inheritance is
| |
|
| |
| used correctly.
| |
|
| |
| It states that, in a computer program, if S is a subtype of T, then
| |
|
| |
| objects of type T may be replaced with objects of type S (i.e., objects of
| |
|
| |
| type S may be substitutes for objects of type T) without altering any of
| |
|
| |
| the desirable properties of that program (correctness, task performed,
| |
|
| |
| etc.).
| |
|
| |
| If for each object o1 of type S there is an object o2 of type T such that
| |
|
| |
| for all programs P defined in terms of T, the behavior of P is unchanged
| |
|
| |
| when o1 is substituted for o2, then S is a subtype of T.
| |
|
| |
| In less formal terms, it says that if a client (program) expects objects of
| |
|
| |
| one type to behave in a certain way, then it’s only okay to substitute
| |
|
| |
| objects of another type if the same expectations are satisfied.
| |
|
| |
| Consider the following example which satisfies the Liskov Substitution
| |
|
| |
| Principle
| |
|
| |
| class Bird
| |
| {
| |
| String name;
| |
| void fly()
| |
| {
| |
| ...
| |
| }
| |
| void altitude()
| |
| {
| |
| ...
| |
| }
| |
| }
| |
|
| |
| class Sparrow extends Birds
| |
| {
| |
|
| |
| }
| |
|
| |
| Example which does not satisfy the Liskov Substitution Principle
| |
|
| |
| class Bird
| |
| {
| |
| String name;
| |
| void fly()
| |
| {
| |
| ...
| |
| }
| |
| void altitude()
| |
| {
| |
| ...
| |
| }
| |
| }
| |
| class Penguin extends Birds
| |
| {
| |
| void fly()
| |
| {
| |
| throw new Exception();
| |
| }
| |
|
| |
| void altitude()
| |
| {
| |
| throw new Exception();
| |
| }
| |
|
| |
| }
| |
| If an override method does nothing or just throws an exception, then you’re
| |
|
| |
| probably violating the LSP.
| |
| Therefore,this does not satisfy the Liskov Substitution Principle though
| |
|
| |
| penguin "is-a" bird.
| |
|
| |
| /* This is a direct lift off so we need to paraphrase it*/
| |
|
| |
| The Liskov Substitution Principle in duck typed languages
| |
| In programming languages with duck typing (e.g. Ruby, Python, Smalltalk)
| |
|
| |
| classes don’t really define types. There is no type checking when assigning
| |
|
| |
| objects to variables or passing objects as method arguments. There is a
| |
|
| |
| kind of type checking when a method is called. It is checked that the
| |
|
| |
| method exists with a matching number of parameters.
| |
| Since classes don’t define types, inheritance in duck typed languages has
| |
|
| |
| nothing to do with the subtype relation or the LSP.
| |
| In a way clients define interfaces in an implicit way. Let’s look at an
| |
|
| |
| example:
| |
| def my_method1(a) do
| |
| a.m1()
| |
| a.m2()
| |
| my_method2(a)
| |
| end
| |
|
| |
| def my_method2(a) do
| |
| a.m3()
| |
| end
| |
| Calling my_method1 with an object a will succeed if the object a provides
| |
|
| |
| the three methods m1, m2 and m3. This is the implicit interface the object
| |
|
| |
| a needs to implement. And with this implicit interface comes the clients
| |
|
| |
| expectation about the behaviour of m1, m2 and m3. That’s just the same as
| |
|
| |
| with the Java interface. If an object b provides m1, m2 and m3 but doesn’t
| |
|
| |
| fit the expected behaviour, the LSP is violated.
| |
|
| |
|
| |
| The LSP isn’t tied to inheritance or class based typing. It applies to duck
| |
|
| |
| types languages as well as to systems without inheritance. LSP is a concept
| |
|
| |
| that applies to all kinds of polymorphism. Only if you don’t use
| |
|
| |
| polymorphism of all you don’t need to care about the LSP.
| |
|
| |
| ===References===
| |
| http://publib.boulder.ibm.com/infocenter/comphelp/v7v91/index.jsp?topic=%2Fcom.ibm.aix.cbl.doc%2Ftpoot30.htm
| |
|
| |
| http://courses.csail.mit.edu/6.170/old-www/2001-Spring/recitations/recitation4.html
| |
Subclassing
Subclassing is a principle of creating a specialization(subclass/ derived class) of a base class(superclass/ parent class) by inheriting the methods and
instance data from the baseclass.
Why do we Subclass?
Code reuse
Specialization: A subclass can define new methods it's superclass does not handle.
Method Overriding: An overridding method can either have minor modifications or be completely changed from its parent class' implementation.
Is Subclassing same as Subtyping?
Subtyping
A is said to be a type of B if A's specification is same as B's. Subtypes should satisfy the Liskov Substitution Principle which states,
If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is
substituted for o2, then S is a subtype of T.
In most programming languages,for example java, ruby, C++, subclassing does not essentially mean subtyping. For a class to be a subtype, the subclass must
follow the Liskov Substitution Principle. However it is easy to override a method with a completely new implementation and fewer constraints. This is not
checked by the compiler and we can create subclasses which are not subtypes. This is considered a bad approach, one of the reasons being, an argument to a
method may be declared of one class A, but the method may be called with an argument of some subclass B. If we do not know if B is a true subtype of A, then
we cannot assume that the behavior guaranteed by A is actually guaranteed by B, and so we cannot reason locally about this method[link mit reference].
Subclass not Subtype
class A {
int x;
int get_x()
{
return x;
}
int sum(A a) { return x + a.x }
}
class B {
int y;
int get_y()
{
return y;
}
int sum(B b)
{
if(y > 0)
return x + b.x + y + b.y;
else
return 0;
}
}