Inheritence in Object-Oriented-Programming

Introduction

The main addition that OOP adds to component-based programming is inheritence. OOP allows defining a class incrementally by extending existing classes.

While using inheritence, it must be taken good care of. It may lost control if your application doesn't use good design patterns.

Inheritence Graph

Inheritence is a way to construct new classes from existing classes.

  • A method in class C overrrdes any method with the same label in all of C's superclass.
  • A class that inherits from exactly one class is said to use single inheritence. Inheritence from more than one class is called multiple inheritence.
  • A class hierarchy with the superclass relation can be seen as a directed graph with the current class being the root.
  • The inheritence is directed and acyclic, there cannot be any inherit cicle in the inheritence hierachy.
  • After striking out all overridden methods, each remaing method should have a unique label.

Method Access Control

When executing inside an object, we often want to call another method in the same object, do a kind of recursive invocation.

We need two ways to do a recursive call. They are called static binding and dynamic binding.

Both of them are needed when using inheritence to override methods.

Dynamic Binding

Dynamic binding allows the new class to correctly extend the old class by letting old methods can new methods. Even the new method did not exist whtn the old method is defined.

This is written as {self M}. This chooses the method matching M that is visible in the current object.

Static Binding

Static binding allows the new methods call the old methods when they have to.

This is written C, M (with a comma), where C is a class that defines a method matching M. This chooses the method matching M tgat us visible in class C.

Encapsulation Control

The principle of controlling encapsulation in an object-oriented language is to limit access to class members (attributes and methods) according to the requirements of the application architecure.

Private and Public Scopes

The two most basic scopes are private and public.

  • A private member is one which is only visible in the object instance. The object instance can see all members defined in its class and its superclasses.
  • A public member is one which is visible everywhere in the program.

These definitions of private and public are natural if classes are used to construct data abstractions.

  1. A class is not the same thing as the data abstractions it defines. The class is increment.
  2. Attributes are internal to the data abstraction and should be invisible from the outside. This is exactly the definition of private scope.
  3. Methods are make up the external interface of the data abstraction, so they should be visible to all entities that reference the abstraction. This is exactly the definition of public scope.

Private Methods

When a method head is a name value, then its scope limited to all instances of the class, but not to the subclasses or their instances. These method is only visible inside the class definition.

class C  
    meth A(X)
        % Method body
    end
end  

And with ! we can capture the method outside the scope.

local  
    A={NewName}
in  
    class C
        meth !A(X)
            % Method body
        end
    end
end  

This creates a name at class definition time.

Protected Methods

In C++, method is protected if it is accessible only in the class it is defined or in descendant classes (and all instances of this classes).

Forwarding and Delegation

Inheritence is one way to reuse functionality when defining new functionality. But it can be tricky to use, because it implies a tight binding between classes.

When developing a application, we want to decomposition different components, always inheritence is not a greate choice for this reason.

Sometimes we use looser approaches. Two such approaches are forwarding and delegation.

Forwarding

An object can forward any message to another object. In this system, we implement this strategy in otherwise method.

local  
    class ForwardMixin
        attr Forward:none
        meth setForward(F) Forward:= F end
        meth otherwise(M)
            if @Forward==none then raise undefinedMethod end
            else {@Forward M} end
        end
    end
in  
    fun {NewF Class Init}
        {New class $ from Class ForwardMixin end Init}
    end
end  

Objects created with NewF have a method setForward(F) that lets them set dynamically the object to which the object will forward messages if they do not understand.

class C1  
    meth init skip end
    meth cube(A B) B=A*A*A end
end

class C2  
    meth init skip end
    meth square(A B) B=A*A end
end  
Obj1={NewF C1 init}  
Obj2={NewF C2 init}  
{Obj2 setForward(Obj1)}

When {Obj2 cube(10 X)} is called, Obj2 forward this message to Obj1 and bind the result to X.

Delegation

Delegation is powerful way to struture a system dynamically. It lets us build a hierachy among objects instead of among classes.

Delegation can achieve the same effects as inheritence, with two main differences, but with objects instead of classes, and can be changed at any time.

local  
    SetSelf={NewName}
    class DelegateMixin
        attr this Delegate:none
        meth !SetSelf(S) this:=S end
        meth set(A X) A:=X end
        meth get(X ?X) X=@A end
        meth setDelegate(D) Delegate:=D End
        meth Del(M S) SS in
            SS:=this this:=S
            try {self M} finally this:=SS end
        end
        meth call(M) SS in
            SS:=this this:=self
            try {self M} finally this:=SS end
        end
        meth otherwise(M)
            if @Delegate==none then
                raise undefinedMethod end
            else
                {@Delegate Del(M @this)}
            end
        end
    end
in  
    fun {NewD Class Init}
        Obj={New class $ from Class DelegateMixin end Init}
    in
        {Obj SetSelf(Obj)}
        Obj
    end
end  

If there are two objects Obj1 and Obj2, suppose there exists a method setDelegate such that {Obj2 setDelegate(Obj1)} sets Obj2 to delegate to Obj1. And Obj1 behaves like Obj2's superclass.

When we enter the Del method, we should preserve our this value in a temp variable. Because the {Self M} should executes in the Obj1 context and change Obj1 attributes.

class C1  
   attr i:0
   meth init skip end
   meth inc(I)
      {@this set(i {@this get(i $)}+I)}
   end
   meth browse
      {@this inc(10)}
      {Browse c1#{@this get(i $)}}
   end
   meth c {@this browse} end end
Obj1={NewD C1 init}  
class C2  
   attr i:0
   meth init skip end
   meth browse
      {@this inc(100)}
      {Browse c2#{@this get(i $)}}
   end
end  
Obj2={NewD C2 init}  
{Obj2 setDelegate(Obj1)}

If we execute

{Obj1 call(browse)}
{Obj2 call(browse)}

% c1#10 c2#100

But if we change the Del procedure like this:

meth Del(M S) in  
    {self M}
end  

The previous execution will browse

c1#100 c2#0  

Because the Obj1 does not preserve its attributes, this in {self M} means Obj1 instead of Obj2. This make no sense, delegation indeed is design pattern that let other handle the message and deal with own attributes. If we do not save the context in Del, it just like send message to Obj2.

Reflection

A system is reflection if it can inspect part of its execution while running.

Reflection can be purely introspective or intrusive.

  • Purely Introspective
    • Only reading the internal state without modifying it.
  • Instrutive
    • Both reading and modifying the internal state.

Meta-Object Protocols

The description of how an object system works at a basic level is called meta-object protocol. The ability to change the meta-object protocol is a powerful way to modify an obejct system. It is used for many purposes: debugging, customzing, and separation of concerns.

Methig Wrapping

A common use of meta-object protocol is to do method wrapping. We can write a tracer to track the behavior of an object-oriented program.

fun {TraceNew Class Init}  
    Obj={New Class Init}
    proc {TraceObj M}
        {Browse entering({Label M)}
        {Obj M}
        {Browse exiting({Label M)}
    end
in TraceObj end  

If an object is created with this procedure, every method will be traced.

A second way is to implement this with a class instead of a procedure.

fun {TraceNew2 Class Init}  
   Obj={New Class Init}
   TInit={NewName}
   class Tracer
      meth !TInit skip end
      meth otherwise(M)
     {Browse entering({Label M})} {Obj M}
     {Browse exiting({Label M})}
      end
   end
in {New Tracer TInit} end  

This strategy is used dynamic class creation, the otherwise method and a freshname TInit.

Reflection of object state

We would like to ba able to read and write the whole state of an object, independant of the object's class.

Draveness

继续阅读此作者的更多文章

comments powered by Disqus