Download - Oop Presentation

Transcript
Page 1: Oop Presentation

Object Oriented Programming - Essential Techniques

S G [email protected]

Page 2: Oop Presentation

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.

Page 3: Oop Presentation

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#)

Page 4: Oop Presentation

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.

Page 5: Oop Presentation

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;}

Page 6: Oop Presentation

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

Page 7: Oop Presentation

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(); }}

Page 8: Oop Presentation

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

Page 9: Oop Presentation

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;}

Page 10: Oop Presentation

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)

Page 11: Oop Presentation

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();

}}

Page 12: Oop Presentation

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

Page 13: Oop Presentation

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.

Page 14: Oop Presentation

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();

Page 15: Oop Presentation

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

Page 16: Oop Presentation

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);

}}

Page 17: Oop Presentation

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

Page 18: Oop Presentation

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!

Page 19: Oop Presentation

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.

Page 20: Oop Presentation

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.

Page 21: Oop Presentation

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); }}

Page 22: Oop Presentation

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

Page 23: Oop Presentation

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.

Page 24: Oop Presentation

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;

}

Page 25: Oop Presentation

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;

}

Page 26: Oop Presentation

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);

Page 27: Oop Presentation

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.

Page 28: Oop Presentation

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)

Page 29: Oop Presentation

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

};

Page 30: Oop Presentation

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;

}

Page 31: Oop Presentation

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);

Page 32: Oop Presentation

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)

Page 33: Oop Presentation

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); }}

Page 34: Oop Presentation

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)

Page 35: Oop Presentation

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

Page 36: Oop Presentation

Q & A

Page 37: Oop Presentation

Thank you!


Top Related