Graduate Program KB

Chapter 6 - Objects and Data Structures


Summary

  • It's key to distinguish the two concepts and use the one more suited to the task at hand.

Objects

  • Expose behavior and hide data.
  • Easy to add new kinds of objects without changing existing behavior.
  • Hard to add new behaviors to existing objects.
  • If the system requires the flexibility to add new data types, we prefer objects.

Data Structures

  • Expose data and have no specific behaviors.
  • Easy to add new behaviors to existing data structures.
  • Hard to add new data structures to existing functions.

Data / Object Anti-Symmetry

  • Objects hide their data behind abstractions and expose functions that operate on that data.
  • Data structures expose their data and have no meaningful functions.
  • Below are 2 examples showing this juxtaposition and to demonstrate that one is not better than the other.

Procedural Example

  • If you add perimeter() function to Geometry, the shape classes, along with any other dependant classes will be unaffected.
  • However if you add a new shape, you must change all the functions in Geometry to deal with it.
public class Square {
    public Point topLeft;
    public double side;
}

public class Rectangle {
    public Point topLeft;
    public double height;
    public double width;
}

public class Circle {
    public Point center;
    public double radius;
}

public class Geometry {
    public final double PI = 3.14;

    public double area(Object shape) throws NoSuchShapeException {
        if (shape instanceof Square) {
            Square s = (Square)shape;
            return s.side * s.side;
        } else if (shape instanceof Rectangle) {
            Rectangle r = (Rectangle)shape;
            return r.height * r.width;
        } else if (shape instanceof Circle) {
            Circle c = (Circle)shape;
            return PI * c.radius * c.radius;
        }
        throw new NoSuchShapeException();
    }
}

OO Example

  • Now the area() method is polymorphic.
  • No Geometry class is needed.
  • If I add a new shape, no existing functions are affected.
  • If I add a function, all the existing shapes must change.
public class Square implements Shape {
    private Point topLeft;
    private double side;

    public double area() {
        return side*side;
    }
}

public class Rectangle implements Shape {
    private Point topLeft;
    private double height;
    private double width;

    public double area() {
        return width*height;
    }
}

public class Circle implements Shape {
    private Point center;
    private double radius;
    private final double PI = 3.14;

    public double area() {
        return PI*radius*radius;
    }
}

Law of Demeter

  • A module should not know about the innards of the objects it manipulates.
  • LoD says that a method f of a class C should only call the methods of these:
    • C
    • An object created by f
    • An object passed as an argument to f
    • An object held in an instance variable of C

Train Wrecks

  • Chains of calls are often considered sloppy style and should be avoided.
final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();

// This can be better written as

Options opts = ctxt.getOptions;
File scratchDir = opts.getScratchDir();
final String outputDir = scratchDir.getAbsolutePath();
  • In terms of LoD; if ctxt, Options and ScratchDir are objects it violates the law due to their internal structure not being hidden.
  • If they are data structures, they naturally expose their innards and LoD does not apply.

Hybrids

  • Avoid half object half data structure implementations, you get the worst of both worlds.
  • Tells the reader that the author has a muddled design.

Hiding Structure

  • Going back to the previous example if ctxt, options, and scratchDir are objects with real behavior.
  • We can hide its innards by telling to do something rather than asking for its internals.
BufferedOutputStream bos = ctxt.createScratchFileStream(classFileName);

Data Transfer Objects (DTO)

  • Is essentially a class with no functions and only public variables.
  • Very useful for communicating with databases and parsing messages from sockets.
  • They often become the first in a series of translation stages that convert raw data in a database into objects in the application code.

Return