Graduate Program KB

What is object orientation?

  • Combination of data and functions
    • f(o) is the same thing as o.f()
    • We've always combined data and functions
  • Encapsulation, Inheritance, Polymorphism

Encapsulation

  • Private and public class fields/methods
  • Can draw borders between disparate parts of the application
    • Limits consideration of what can affect one part to its interface
  • Could do this in c
    • Headers define the interface, .c files contain the implementation
    • Details left out of the header aren't available to other code
    • point.h
      struct Point;
      struct Point * makePoint(double x, double y);
      double distance(struct Point *p1, struct Point *p2);
      
    • point.c
      #include "point.h"
      #include <stdlib.h>
      #include <math.h>
      
      struct Point {
      double x,y;
      }
      
      struct Point *makePoint(double x, double y) {
        struct Point *p = malloc(sizeof(struct Point));
        p->x = x;
        p->y = y;
        return p;
      }
      
      double distance(struct Point *p1, struct Point *p2) {
        double dx = p1->x - p2->x
        double dy = p1->y - p2->y;
        return sqrt(dx*dx, dy*dy);
      }
      
    • The Point struct is forward declared in the header, but the actual definition is in the implementation
      • An incomplete type
    • Usage code including the header knows
      1. There is some data with an unknown format called Point
      2. Some functions that will take the type pointer to Point
    • Usage code does not know the size or contents
      • Can't use as a value type, only pointer
    • This is called an opaque pointer
    • Note however, that the struct definition from the implementation could be copied into the usage code to tell it about the format
    • C++ added object orientation
      • Implementation requires data members to be declared in the header
      • This breaks encapsulation
      • While the compiler will prevent access to those variables usage code still needs to read it
    • Java and C# remove the distinction between interface and implementation files further weakening encapsulation
    • Hard to say that OO depends on strong encapsulation

Inheritance

  • Inheritance is the redeclaration of a group of variables and functions within an enclosing scope
  • Can do this in c
  • namedPoint.h
      struct NamedPoint;
      struct NamedPoint *makeNamedPoint(double x, double y, char *name);
      void setName(struct NamedPoint *np, char *name);
      char *getName(struct NamedPoint *np);
    
  • namedPoint.c
    #include "namedPoint.h"
    #include <stdlib.h>
    
    struct NamedPoint {
      double x,y;
      char *name;
    }
    
    struct NamedPoint *makeNamedPoint(double x, double y, char *name) {
      struct NamedPoint *p = malloc(sizeof(struct NamedPoint));
      p->x = x;
      p->y = y;
      p->name = name;
      return p;
    }
    
    void setName(struct NamedPoint *np, char *name) {
      np->name = name;
    }
    
    char *getName(struct NamedPoint *np) {
      return np->name;
    }
    
  • main.c
    #include "point.h"
    #include "namedPoint.h"
    #include <stdio.h>
    
    int main(int ac, char **av) {
      struct NamedPoint *origin = makeNamedPoint(0.0, 0.0, "origin");
      struct NamedPoint *upperRight = makeNamedPoint(1.0, 1.0, "upperRight");
      printf("distance=%f\n",
        distance(
          (struct Point*) origin,
          (struct Point*) upperRight
        )
      );
    }
    
  • NamedPoint starts with the same data members as Point with the same offsets
  • So a pointer to NamedPoint can act as a pointer to Point
  • Common practice in C
  • Multiple inheritance is harder
  • Had to cast NamedPoint to Point - an OO language would do this automatically
  • OO makes inheritance a lot nicer

Polymorphism

  struct FILE {
    void (*open)(char *name, int mode);
    void (*close)();
    int (*read)();
    void (*write)(char);
    void (*seek)(long index, int mode);
  }
  • FILE is a struct that contains a bunch of function pointers - an interface
  • This can be implemented by a device driver to allow any function that takes a FILE struct to perform these operations
    #include "file.h"
    
    void open(char *name, int mode) {/* ... */}
    void close() {/* ... */}
    int read() {/* ... */}
    void write(char c) {/* ... */}
    void seek(long index, int mode) {/* ... */}
    
    struct FILE console = {open, close, read, write, seek};
    
    extern struct FILE *stdin;
    int getchar() {
      return stdin->read();
    }
    
  • Object orientation in C++ is just a virtual table at the start of the object that looks like the FILE for all the virtual methods
  • Polymorphism is just pointers to functions
  • OO made it safer and more convenient
    • Need to initialize all the pointers correctly
    • Need to make all calls through the pointers
    • Bugs can be hard to find
  • OO imposes discipline on indirect transfer of control

Power of polymorphism

  • Have a copy program which copies from an input device to an output device
  • Want to add devices for handwriting recognition and speech synthesis
  • With polymorphism don't even need to recompile the program
    • It just calls interfaces of a particular form and knows nothing about the details
  • UNIX made devices plugins - was common to want to perform the same operations on different devices
    • Decks of cards, magnetic tape
  • Most programmers did not extend this to their own code - pointers to functions are dangerous

Dependency inversion

  • Before OO
    • The main function calls higher level functions
    • Higher level functions call mid level functions
    • Mid level functions call low level functions
    • Source dependencies, imports follow this flow
      • Main has to import the higher level functions so it can call them
    • Flow of control dictated by behaviour
    • Source dependencies dictated by flow of control
  • Polymorphism
    • Can import an interface instead of the concrete implementation
    • Can then use any concrete implementation at runtime
    • Architects have complete control over the direction of dependencies in the system inversion
    • Can rearrange dependencies so that database and UI depend on business rules
    • Can then compile into separate components or deployment units
    • Business rules do not depend on UI and database, they can be updated and deployed separately
    • Independent deployability: only the changed component needs to be reseployed
    • Independent developability: modules which can be deployed separately can be developed separately by different teams

Conclusion

  • OO is the ability to gain control of the direction of every dependency in the system through polymorphism
  • Allows the creation of a plugin architecture which allows higher level components to be independent of lower level ones
  • The lower level components can also be developed and deployed independently of the higher level