Satish annapureddy director technology myrio corporation
This presentation is the property of its rightful owner.
Sponsored Links
1 / 39

Practical object oriented design techniques PowerPoint PPT Presentation


  • 80 Views
  • Uploaded on
  • Presentation posted in: General

Satish Annapureddy Director, Technology Myrio Corporation. Practical object oriented design techniques. Introduction. Focus Practical techniques and guidelines, that can be used daily, to create “good” object-oriented designs. How to design objects – fields and methods

Download Presentation

Practical object oriented design techniques

An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -

Presentation Transcript


Satish Annapureddy

Director, Technology

Myrio Corporation

Practical object oriented design techniques


Introduction

  • Focus

    • Practical techniques and guidelines, that can be used daily, to create “good” object-oriented designs.

      • How to design objects – fields and methods

      • How to design relationships and interactions

        • Inheritance, composition, interfaces

      • Thread-safe design with OO.

      • Java design idioms


Proper intialization

  • Objects can be seen as finite-state machines.

    • Instance variables store state. Methods change state by changing instance variables.

  • The challenge is in keeping the object in a valid state at all times.

    • Data hiding is imperative!

  • Proper initialization ensures that the object starts in a valid state.


Proper Initialization (contd)

  • Design constructors and initializers so that there is no way the object can start in an invalid state.

  • Throw exception to indicate invalid constructor/method parameters eg., java.lang. IllegalArgumentException

  • If for some reason, it is needed to allow the object to start in an invalid state, throw exception to indicate improper usage. eg., java.lang.IllegalStateException.


Proper finalization

  • Finalizers – what they are NOT for -

    • System resources – file handles, sockets, memory etc. are finite. Free them when no longer needed.

      • In Java, garbage collector runs finalizer when it frees unreferenced object (not when running out of some non-memory resource).

      • Provide an API to allow clients to release resources. eg., open() and close().


Proper finalization (contd)

  • Finalizers – what are they for?

    • In Java, for releasing memory allocated in native methods via JNI.

    • Last attempt at releasing non-memory resources.

      • “Attempt” since finalizers may not run on live objects at time of exit.

        • In Java, java.lang.Runtime.runFinalizersOnExit() ensures that finalizers are run on all live objects at exit.


Designing fields

  • Use a variable per purpose.

    • In version 1, let's say that the temperature sensor is capable of tracking temps >= 0;

      public class TemperatureSensor {

      ...

      public int getTemperature() {

      return temp;

      }

      public boolean isSensorWorking() {

      return temp < 0; // dual-use

      }

      private int temp;

      }


Designing fields (contd)

  • In version 2, the sensor can track negative temperatures too.

    public class TemperatureSensor {

    ...

    public int getTemperature() {

    return temp;

    }

    public boolean isSensorWorking() {

    return working == true;

    }

    private int temp;

    private boolean working;

    }


Designing methods

  • Minimizemethod coupling as much as possible.

    • The less a method (and hence a class) knows about other classes, the better.

    • Take in the most relevant object and output the object actually affected.

      • Least coupled are utility methods (static methods that depend only on input parameters and class constants)


Designing methods (contd)

// encode x-www-form-urlencoded strings

public class XWWWFormURLEncoder {

public void encode(URLString str) {

...

}

}

Note: x-www-form-urlencoded is applied to a String, usually in the context of a URL, but not necessarily.


Designing methods (contd)

  • Maximize Cohesion – each method must focus on a single conceptual task. eg., insert(..), delete(..), open(..) etc.

    • Why?

      • Changes localized. Removes side-effects.

      • More readable.

    • How?

      • Avoid passing control parameters. Create multiple methods instead.


Designing methods (contd)

public class Account {

// low cohesion: control data (type) is passed in.

// split into multiple methods: creditAccount(..), debitAccount(..), clearAccount(..)

public void updateBalance(int type, float amount) {

if(type == CREDIT) {...}

else if(type == DEBIT) {...}

else if(type == CLEAR) {...}

else if...

}

}


Encapsulation and information hiding

  • Encapsulation and information hiding are two different concepts!

    • Encapsulation refers to bundling data and operations that use that data.

    • Information hiding refers to hiding implementation details of the class

    • You can have encapsulation without information hiding.

  • Good OO requires both done right!

    • Objects should be intelligent entities. Do not separate methods from related data.


Encapsulation and information hiding (contd)

  • Information hiding guidelines

    • Don't expose data.

    • Don't expose the fact that certain attribute is derived – use getDuration(...) instead of computeDuration(...)

    • Don't expose details on internal data structures – getMap() instead of getTreeMap().

    • Don't give out mutable handles to internal data.


Encapsulation and information Hiding (contd)

public class SortedList {

public void insert(ListElement obj) {

// insert at appropriate position so that

// the list remains sorted.

}

public ListElement getElement(int position) {

return elementAt(position); // direct access to internal data.

}

}

Notes: client code can use getElement().setX(...) so that the list is

no longer sorted.


Encapsulation and Information Hiding (contd)

  • get/set expose implementation details via interfaces

    • For example, code that uses int getX() breaks when the return type is changed to float.

    • Objects should be designed to be intelligent. ie., request services not data.

      • By understanding how the class will be used, you can eliminate most get/set methods by providing services instead.


UI Design without getters and setters

  • Problem: If get/set are removed, an object must somehow know how to present its UI.

    • But, it is not feasible for an object to support all possible UIs.

  • Solution: Use the Builder pattern

    • Use a Builder helper object and provide for export and import via interfaces.

      • This basically moves the get/set to the Builder (UI) object from the business class object.


UI design without getters and setters (contd)

public class TestingStats {

public interface Exporter {

void setScores(float[] scores);

void setAvg(float avg);

..

}

public void export(Exporter builder) {

builder.setScores(scores);

builder.setAvg(avg);

...

}

private float[] scores;

private float avg, median, mean; // and others

...

}


UI Design without getters and setters (contd)

public class TestingStatsUI extends JPanel implements Exporter {

public void setScores(float[] scores) { graph.setData(scores); }

public void setAvg(float avg) { add(new JtextField(“”+avg)); }

...

private SuperPowerfulGraphWidget graph;

}

public void showUI(...) {

...

testingStats.export(testingStatsUI);

...

testingStatsUI.show();

...

}


Thread-safe design

  • Heap (and method area) is common, stacks are local among threads.

  • Methods cause state transitions. But during the transition, the state becomes invalid. Atomicity is required to ensure that the invalid state is not exposed to other threads.

  • Guarding critical sections prevents race conditions – read/write and write/write conflicts.


Thread-safe design (contd)

  • For example, insert() below has both Read/Write and Write/Write conflicts.

    public class LinkedList {

    public void insert(LinkElement new_element) {

    tmp = cur.next;

    cur.next = new_element;

    new_element.next = tmp;

    }

    public void printList() {

    for(LinkElement elem = start; elem != null; elem=elem.next)

    print(elem);

    }

    ...

    }


Thread-safe design (contd)

  • Three approaches

    • Protect critical sections with mutexes.

      • Another reason why fields MUST be private!

      • Identify critical sections and use lock/unlock to enter and leave critical sections.

        • Synchronizing everything degrades performance and may also result in deadlocks.

      • Most common and most powerful approach. Thread coordination (wait and notify) are impossible without locks.

    • Use immutable objects.

      • Inherently thread-safe - object state does not change after creation.


Thread-safe design (contd)

  • Identify critical sections but no locking.

    • Critical sections that read are left alone.

    • Critical sections that write are changed to create new immutable objects to reflect new states.

    • eg., java.lang.String

  • Thread-safe wrappers

    • Front-end the object with a thread-safe wrapper that has the same interface. (Decorator pattern)

    • Flexible – make objects thread-safe only when really needed.

    • Eg., Collections.synchronizedSet(Set set)


  • Exceptions

    • When to throw exceptions?

      • To indicate an abnormal event: If your method encounters an “error condition”, throw an exception.

        • What constitutes an “error condition”?

          • Is EOF an error condition?

            • While reading byte by byte into a buffer?

            • When only 3 bytes of a 4-byte int are read?

      • To indicate broken contract: caller violates pre-condition.

        • What about calling java.util.Iterator.next() when java.util.Iterator.hasNext() returns false?


    Exceptions (contd)

    • A Runtime exception – NoSuchElementException is thrown.

  • What about a method that expects non-null String but is passed null instead?

    • A runtime exception – IllegalArgumentException is thrown.

  • What exception to throw?

    • The client programmer should handle “abnormal” conditions by throwing specific checked exceptions. These are declared and hence enforced by the compiler.

    • Broken contracts indicate bad code and should be fixed before release. Runtime exceptions are appropriate for this purpose.


  • Inheritance vs Composition

    • Inheritance makes use of dynamic binding and polymorphism.

      • Great for adding new behaviors via subclassing.

      • Bad when superclass's interface needs modifications.

        • Changes to public method signatures ripple to all the clients that use the superclass or any of its subclasses.

    • Composition delegates actual implementation to a back-end class.


    Inheritance vs Composition (contd)

    • Composition: Advantages

      • Better isolates back-end changes: The front-end implementation will change to reflect the back-end change, but the front-end interface may remain the same.

      • Subclasses are more rigid than front-end classes. eg., changing return type on an inherited method is not possible.

      • Composition allows optimizations – lazy instantiation of back-end objects and dynamically changing back-end objects.


    Inheritance vs Composition (contd)

    • Composition: Disadvantages

      • Polymorphism makes inheritance far more effective than composition for adding new implementations (new subclasses).

        • Composition with interfaces solves this problem.

      • Delegation approach in composition might affect performance.

  • How to choose between the two?

    • Inheritance ONLY for permanent is-a relationships. Otherwise, use composition.

      • Do not use inheritance just to reuse implementation or for polymorphism


  • Inheritance vs Composition (contd)

    Consider the following classes:

    public class MediaAsset {

    public String getURL() {

    ...

    }

    }

    public class MovieAsset extends MediaAsset { ... }

    public class MoviePlayer {

    public void setupMovie(MovieAsset movie) {

    BandwidthController.reserveBandwidth(authenticationInfo, movie.getURL());

    ...

    }

    }


    Inheritance vs Composition

    Let us say that getURL() method in MediaAsset is changed to return URL.

    public class MediaAsset {

    public StringURL getURL() {

    }

    ...

    }

    • Now, MoviePlayer object is broken since it uses MovieAsset which extends MediaAsset. If we used composition instead, the change would not ripple past MovieAsset (the front-end class)

      public class MovieAsset {

      public String getURL() {

      return mediaAsset.getURL().getPath();

      }

      private MediaAsset mediaAsset;

      }


    Composition with interfaces

    • Interfaces allow for more polymorphism than inheritance.

      • Inheritance with polymorphism allows for using a subclass in place of a superclass.

        • With interfaces you are not limited to one inheritance hierarchy. Any class that implements the interface can be substituted.

      • Composition with interfaces is just as powerful and flexible as inheritance with polymorphism.

      • Interfaces do not suffer from the “diamond” problem.


    Composition with interfaces(contd)

    • No multiple inheritance of implementation (methods and non-private fields) and hence no ambiguity.

  • Interfaces allow implementation to be totally decoupled from interface.

  • How to use interfaces:

    • Various subsystems should communicate with each other via interfaces.

      • Use interfaces to abstract out subsystems that can have many implementations.

    • Use interfaces to represent functionality common to different class hierarchies.


  • Composition with interfaces (contd)

    • In the previous example, MovieAsset cannot be substituted for MediaAsset if it is implemented using composition. By combining composition with interface, we can easily solve this problem!

      public interface Asset {

      String getAssetURL();

      ...

      }

      public class MediaAsset implements Asset { .. }

      public class MovieAsset implements Asset {

      // delegate to mediaAsset instance.

      }


    Implementation inheritance issues

    • Implementation inheritance couples subclass method implementation to superclass even if no data is exposed.

      • Any superclass implementation changes may break the subclasses.

        • Protected member variables are worse!

    • Don't make any assumptions about or use artifacts of superclass implementation

      • Better yet, consider using interfaces.


    Implementation inheritance issues (contd)

    public class Base {

    public void foo() { ... }

    ...

    }

    public class Foo extends Base {

    public void foo() { ... }

    public boolean equals(Object obj) { ... }

    ...

    }

    // foo1 and foo2 are two instances of Foo and foo1.equals(foo2) returns true.

    Hashtable htbl = new Hashtable().put(foo1, “Foo 1”);

    Now, htbl.get(foo2) should return “Foo 1”. But it returns null. Why?


    Abstract base classes and interfaces

    • Combine interfaces with abstract base classes to get the best of both worlds

      • Implement an interface with default implementation in an abstract base class.

      • Class hierarchy is used as a common code repository. If the implementation changes radically, you can go back to using the interface.


    Abstract base class and interfaces (contd)

    public interface RTSPClient {

    void setup(..);

    void teardown(..);

    void play(..);

    void pause(..);

    void describe(..);

    ...

    }

    public abstract class AbstractRTSPClient implements RTSPClient {

    abstract void setup(...); // other methods as well.

    // connection management, keep alive timer impl. Etc.

    }


    Abstract base classes and interfaces (contd)

    public class NcubeRTSPClient extends AbstractRTSPClient {

    void setup(...) {

    // compose request headers

    // send request and receive response

    // activate keep-alive based on session timeout in response

    // update state machine

    }

    }

    public class BitBandRTSPClient implements RTSPClient {

    void setup() { bitband_setup(...); }

    native void bitband_setup(...); // RTSP and more is done in native library

    }


    References

    • OODP/Java design articles by Bill Venners at http://www.artima.com

    • OODP/Java design articles by Allen Holub at http://www.holub.com

    • OODP series at http://www.javaworld.com/channel_content/jw-oop-index.shtml


  • Login