oop presentation

Post on 18-Dec-2014

1.710 Views

Category:

Technology

1 Downloads

Preview:

Click to see full reader

DESCRIPTION

 

TRANSCRIPT

Object Oriented Programming - Essential Techniques

S G Ganeshsgganesh@gmail.com

Language Pragmatics

A language is not just syntax and learning a language isn’t just learning to program.

Mastering a language requires good understanding of language semantics, pragmatics, traps and pitfalls with considerable experience in programming and design using that language.

Five Specific OO Tips and Techniques

We’ll see 5 specific tips/techniques Based on understanding, experience and

usage of language features Tips are about pragmatics of using

language features The mistakes covered in the tips are errors

in usage Examples in C++ and Java (sometimes in

C#)

1. Avoid calling virtual functions in constructors

Constructors do not support runtime polymorphism fully as the derived objects are not constructed yet when base class constructor executes.

So, avoid calling virtual functions from base-class constructors, which might result in subtle bugs in the code.

C++ resolves virtual function calls to base type

struct base {base() {

vfun(); } virtual void vfun() {

cout << “Inside base::vfun\n”; }

};struct deri : base { virtual void vfun() {

cout << “Inside deri::vfun\n”; }

};int main(){ deri d;}

C++ resolves virtual function calls to base type

struct base {base() {

vfun(); } virtual void vfun() {

cout << “Inside base::vfun\n”; }

};struct deri : base { virtual void vfun() {

cout << “Inside deri::vfun\n”; }

};int main(){ deri d;}

// prints:Inside base::vfun

Java/C# resolves virtual function calls dynamically

// Java exampleclass base { public base() { vfun(); } public void vfun() { System.out.println("Inside base::vfun"); }}class deri extends base { public void vfun() { System.out.println("Inside deri::vfun"); } public static void main(String []s) { deri d = new deri(); }}

Java/C# resolves virtual function calls dynamically

// Java exampleclass base { public base() { vfun(); } public void vfun() { System.out.println("Inside base::vfun"); }}class deri extends base { public void vfun() { System.out.println("Inside deri::vfun"); } public static void main(String []s) { deri d = new deri(); }}

// prints: Inside deri::vfun

In C++, pure virtual methods might get called

struct base {base() {

base * bptr = this; bptr->bar(); // even simpler ... ((base*)(this))->bar();

} virtual void bar() =0;};struct deri: base {

void bar(){ }};int main() {

deri d;}

In C++, pure virtual methods might get called

struct base {base() {

base * bptr = this; bptr->bar(); // even simpler ... ((base*)(this))->bar();

} virtual void bar() =0;};struct deri: base {

void bar(){ }};int main() {

deri d;}// g++ output: // pure virtual method called// ABORT instruction (core dumped)

Dynamic method call in Java might lead to trouble// Java code class Base {

public Base() { foo();

} public void foo() {

System.out.println("In Base's foo ");}

}class Derived extends Base {

public Derived() { i = new Integer(10);

}public void foo() {

System.out.println("In Derived's foo " + i.toString() );}

private Integer i; }class Test {

public static void main(String [] s) {new Derived().foo();

}}

Dynamic method call in Java might lead to trouble// Java code class Base {

public Base() { foo();

} public void foo() {

System.out.println("In Base's foo ");}

}class Derived extends Base {

public Derived() { i = new Integer(10);

}public void foo() {

System.out.println("In Derived's foo " + i.toString() );}

private Integer i; }class Test {

public static void main(String [] s) {new Derived().foo();

}}// this program fails by throwing a NullPointerException

2. Preserve the basic properties of methods while overriding

Overriding the methods incorrectly can result in bugs and unexpected problems in the code.

Adhering to Liskov’s Substitution Principle is possible only when overriding is done properly.

Make sure that the method signatures match exactly while overriding is done

Provide semantics similar to the base method in the overridden method.

In C++, provide consistent default parameters

struct Base {virtual void call(int val = 10)

{ cout << “The default value is :”<< endl; }};

struct Derived : public Base {virtual void call(int val = 20)

{ cout << “The default value is :”<< endl; }};

// user code:Base *b = new Derived;b->call();

In C++, provide consistent default parameters

struct Base {virtual void call(int val = 10)

{ cout << “The default value is :”<< endl; }};

struct Derived : public Base {virtual void call(int val = 20)

{ cout << “The default value is :”<< endl; }};

// user code:Base *b = new Derived;b->call();

// prints: // The default value is: 10

In Java, final might be removed while overriding

class Base {public void vfoo(final int arg) {

System.out.println("in Base; arg = "+arg); }}class Derived extends Base {public void vfoo(int arg) { arg = 0; System.out.println("in Derived; arg = "+arg); } public static void main(String []s) { Base b = new Base(); b.vfoo(10); b = new Derived(); b.vfoo(10);

}}

In Java, final might be removed while overriding

class Base {public void vfoo(final int arg) {

System.out.println("in Base; arg = "+arg); }}class Derived extends Base {public void vfoo(int arg) { arg = 0; System.out.println("in Derived; arg = "+arg); } public static void main(String []s) { Base b = new Base(); b.vfoo(10); b = new Derived(); b.vfoo(10);

}}// prints:// in Base; arg = 10 // in Derived; arg = 0

Provide consistent exception specification

struct Shape {// can throw any exceptionsvirtual void rotate(int angle) = 0; // other methods

};

struct Circle : public Shape {virtual void rotate(int angle) throw (CannotRotateException) {

throw CannotRotateException(); }// other methods

};

// client code Shape *shapePtr = new Circle();shapePtr->rotate(10);// program aborts!

3. Beware of order of initialization problems.

Many subtle problems can happen because of order of initialization issues.

Avoid code that depends on particular order of implementation as provided by the compiler or the implementation.

In C++, such init can cause unintuitive results

// translation unit 1 int i = 10;

// translation unit 2 extern int i;int j = i;// j is 0 or 10?// depends on the compiler/link line.

In Java, such init can cause unintuitive results

class Init { static int j = foo(); static int k = 10; static int foo() { return k; } public static void main(String [] s) { System.out.println("j = " + j); }}

In Java, such init can cause unintuitive results

class Init { static int j = foo(); static int k = 10; static int foo() { return k; } public static void main(String [] s) { System.out.println("j = " + j); }}

// prints// j = 0

4. Avoid switch/nested if-else based on types

Programmers from structured programming background tend to use extensive use of control structures.

Whenever you find cascading if-else statements or switch statements checking for types or attributes of different types to take actions, consider using inheritance with virtual method calls.

C# code to switch based on types

public enum Phone {Cell = 0, Mobile, LandLine

} // method for calculating phone-charges public static double CalculateCharges(Phone phone, int seconds){

double phoneCharge = 0;switch(phone){case Phone.Cell:

// calculate charges for a cell case Phone.Mobile:

// calculate charges for a mobile case Phone.LandLine:

// calculate charges for a landline }return phoneCharge;

}

C# code with if-else using RTTI

abstract class Phone {// members here

}class Cell : Phone {

// methods specific to cells}// similar implementation for a LandLine public static double CalculateCharges(Phone phone, int seconds){

double phoneCharge = 0;if(phone is Cell){

// calculate charges for a cell }else if (phone is LandLine){

// calculate charges for a landline }return phoneCharge;

}

C# code: Correct solution using virtual functions

abstract class Phone {public abstract double CalculateCharges(int seconds);// other methods

}class Cell : Phone {

public override double CalculateCharges(int seconds){// calculate charges for a cell

}}

// similar implementation for a LandLine// Now let us calculate the charges for 30 secondsPhone ph = new Cell ();ph.CalculateCharges(30);

5. Avoid hiding of names in different scopes.

Hiding of names in different scopes is unintuitive to the readers of the code

Using name hiding extensively can affect the readability of the program.

Its a convenient feature; avoid name hiding as it can result in subtle defects and unexpected problems.

Hiding of names can happen in different situations

The name in the immediate scope can hide the one in the outer scope (e.g. function args and local variables)

A variable in a inner block can hide a name from outer block (no way to distinguish the two)

Derived class method differs from a base class virtual method of same name in its return type or signature - rather it is hidden.

Derived member having same name and signature as the base-class non-virtual non-final member; the base member is hidden (e.g. data members)

C++ examples for name hiding

// valid in C++, error in Java/C#void foo { // outer block

int x, y;{ // inner block

int x = 10, y = 20;// hides the outer x and y

}}

// C++ Code int x, y; // global variables x and ystruct Point {

int x, y; // class members x and y Point(int x, int y); // function arguments x and y

};

C++/Java/C# example for a bug with hiding

// Bug in C++, Java and C# Point(int x, int y) {

x = x;y = y;

}

// C++Point(int x, int y) {

this->x = x;this->y = y;

}// Java and C# Point(int x, int y) {

this.x = x;this.y = y;

}

C++: No overloading across scopes

struct Base {void foo(int) {

cout<<"Inside Base::foo(int)";}

};

struct Derived : public Base {void foo(double) {

cout<<"Inside Derived::foo(double)";}

};

Derived d;d.foo(10);

C++: No overloading across scopes

struct Base {void foo(int) {

cout<<"Inside Base::foo(int)";}

};

struct Derived : public Base {void foo(double) {

cout<<"Inside Derived::foo(double)";}

};

Derived d;d.foo(10);// prints: // Inside Derived::foo(double)

Java: Overloading across scopes!

class base { public void foo(int i) { System.out.println("In Base::foo(int)"); }}

class deri extends base { public void foo(double i) { System.out.println("Inside deri::foo(double)"); } public static void main(String []s) { deri d = new deri(); d.foo(10); }}

Java: Overloading across scopes!

class base { public void foo(int i) { System.out.println("In Base::foo(int)"); }}

class deri extends base { public void foo(double i) { System.out.println("Inside deri::foo(double)"); } public static void main(String []s) { deri d = new deri(); d.foo(10); }}// prints: Inside Base::foo(int)

How to write robust code and avoid defects?

Many of the language rules, semantics and pragmatics are unintuitive

What can help in detecting bugs early? Tools (but of limited extent) Extensive testing Peer review Good knowledge and experience

No other approach can create robust code than passion towards writing excellent code

Q & A

Thank you!

top related