Design Computation
Render as Slideshow | Accordion

Multiplicity 2

This module covers a more sophisticated means of creating complexity, through classes of objects.

Readings

Assignment

See the multiplicity with objects assignment.

Classes and Objects

Each fish is an “object” of class “Fish.”

Object-Oriented Programming (OOP) is the most common programming paradigm used today.

Outline

1. Introduction to Object-Oriented Programming
1.1 OOP: What is it?
1.2 Definitions
1.3 Examples
2. Defining Classes in Processing
2.1 Class Syntax
2.1.1 Creating an “instance” of a class
2.1.2 Using Objects (dot notation)
2.2 Multiplicity with Objects
3 Details of Objects and Classes
3.1 Inheritance
3.2 Polymorphism

Programming Paradigms

There are several categories of programming methods.

Procedural Programming

This is what we’ve been doing so far. Procedural programming conceives of computation as a set of sequentially executed steps, and the programs that are written reflect that structure. This is the form of most scripting, although we may leverage the power of objects when programming this way.

“Spaghetti Programming” is procedural programming gone wrong. These programs have become so complicated that they are nearly unreadable, jumping from one line to another so often that they lose the benefits of structure.

Functional Programming

This method conceives of a program as a set of mathematical functions to be executed, similar to the lambda calculus, where functions have inputs (arguments) and outputs (return values) and statements are executed as functions of functions.

Object-Oriented Programming

This method enables a user to define custom data types. These types are called “classes,” and an instance of a class is called an “object.” These objects contain state data, that determine the properties of the object at any given time, and a set of functions (called “methods”) that change that data.

What is OOP?

Object-oriented programming, or OOP, forms the basis of all major programming languages and the foundation for every mainstream application today. You use objects all the time in Windows. Every window, button, toolbar, menu item, icon, and scrollbar are objects.

Objects form practically everything you encounter in any major graphical operating system: Windows, Linux, MacOS, etc. Lines, polylines, surfaces, and meshes in Rhino are objects. If you look under “Properties,” one of the headings is “object type.”

OOP will allow us to model the world and to think in higher-level terms. This means we can think closer to terms we use everyday, terms that approach English and get farther away from the abstracted vocabulary of computers.

In OOP, we also conceive of everything as objects, even abstract level data structures, such as nodes and links in a graph data structure, not just things that we can see rendered on a screen. In a pseudo-physics system, forces, springs, particles, and the system itself are classes of objects.

A Difference in Expression

Thus far, we’ve been programming with verbs. We’ve told Processing to draw a line, change the stroke color, clear the screen, etc. In the end, this only gets us so far. This is procedural programming.

Ideally, programs should reflect your thinking. As it turns out, more often it makes sense to think in terms of nouns instead of verbs, or rather, of objects, their properties, and actions that manipulate those objects.

Object-oriented programming (OOP) is such a system.

Object-oriented programming allows us to think in terms of things first, what their properties and characteristics are, and how they behave and respond to stimuli.

Gui Objects As Examples

For instance, all toolbar buttons in Windows look pretty much the same. They are all square and each have an icon that indicates what it does. When you put the mouse cursor over one, a tooltip pops up that describes in words what it does. When you press a mouse button while the mouse cursor is over it, its appearance changes that makes it look as if it is depressed. And when you release, it changes back to its original appearance and executes the command.

OOP allows the programmer to write the code that describes these buttons once, and then to produce hundreds of them on command, reusing the code that describes the general behavior.

The programmer can also extend that default appearance and behavior easily by encoding only what’s different. Another toolbar button may be larger, have a slightly different appearance, etc. With OOP, the programmer doesn’t need to rewrite the entire class to create variations.

More Real Examples

Example of a base (or parent) class Person, a derived (or child) class Architect, and an instance of an Architect.

When we define a “class” of “objects,” we are really describing a template for creating something. Once we have that template, we can create multiple “instances” of those objects, which are independent version of the thing described by the template.

We could, for example, describe a Person class in Processing, give it a way to draw itself, give it a name, SSN, etc.

User-Defined Types

A class is really a “user-defined data type,” where the user can create a piece of data, described by other, more fundamental pieces of data.

Fundamental Types

We have talked about ints, floats, Strings, and booleans so far as “fundamental data types.” These are raw data types provided by Processing as a basis for describing data numerically and with strings of characters.

When we specify a type for our data, like an integer, more comes along with that description that just the value of the integer. We know that we can add them together, subtract them from each other, multiply them, sometimes divide, and compare them to other integers.

Hence, a type is more than just a value, but also a whole description about the rules used to manipulate that value.

Arrays of Data

Then we talked about arrays, which are really defined as a series of another data type, like “array of ints” or “array of floats.” We can talk about arrays in general, but when we actually use one, we have to tell Processing what the array is supposed to contain.

This data type is more complex than a regular int, and with it comes along rules about creation, indexing, etc., and a syntax to allow us to perform those operations.

Example: Vector

Sometimes in mathematics, physics, and structures, we need to describe quantities that have a more complicated nature than just floats and ints. To describe forces, for example, we use vectors from mathematics, which are described as having a direction, magnitude, and point of application.

How do we describe this in Processing? Well, we actually can describe each part of the vector as a fundamental data type. In the Cartesian model of vectors, we could use two or three floats to describe each of the x-, y-, and sometimes z- components of the vector. In the polar model, we need a float that describes the angle (maybe two!) and another float to describe the magnitude.

Along with this, we would need to describe addition, subtraction, and two types of multiplication (cross and dot product).

Types from Types

Thus, in describing a class of objects, we are really using other data types in combination with each other to describe more sophisticated thing, like a Person, Vector, etc.

We then give it mechanisms for manipulating those values, or performing actions with them, like drawing.

Definitions

Class

a category of objects, or a set of things with their defining properties. It is really a user-defined data type.

Just like variables are given a type, such as int, float, boolean, you can use object-oriented programming to customize your own types and be much more specific than the generic types in Processing.

Object

an “instance” of a particular type.

Method

a function as part of the class’s definition that describes an action that affects the object’s data. A class definition typically has many methods.

Instance Variable

also called a field, a variable inside the class definition that describes the state of the object. This would be like the “name” of a Person object, which itself might be a String or an instance of another kind of object.

Instantiation

the process of creating a single object. After you define a class, you must instantiate an object of that class.

Inheritance

the creation of a class of related objects based on the fundamental behavior of another, existing, “parent” class.

We can say that a class is “derived” from another class. If we have a class called Person, which has fundamental data about a human’s body shape, number of legs, mode of walking, etc., an example of a “derived” class would be Architect or Student, which has the added property of a profession. Thus, an Architect class will “inherit” from a Person class.

Polymorphism

the property of derived objects that allows them to be treated as a parent object.

With the above example, Person and Architect classes, anything that we can do to a Person object, we can also do to an Architect object. Thus any function that accepts a Person as a parameter, we can pass it an Architect instead and still get a valid result.

Encapsulation

a formal term that describes the way the code is written for a class definition, that everything needed to describe the class and manipulate the class data is contained in the class definition alone. A properly “encapsulated” class doesn’t depend on global variables or external functions.

Agents and Components

Object-Oriented Programming is clearly manifested in distributed systems.

Chris Reynolds
Boids
One of the original distributed systems demonstrating flocking behavior, an emergent complexity from programming objects with simple behaviors. These Boids are instances of a Boid class, which describes the behaviors of each class.

Class Declaration Syntax

Class declarations are templates that contain all the “instance variables” and “methods” that comprise an object.

  1. They start with the keyword “class” and are followed by the name of the class, which always begins with a capital letter.
  2. They are followed by a code block, which is delineated of course by a pair of braces.
  3. Next come the instance variables, which contain the “state” of the object.
  4. Then the “constructor” method, which initializes the object on creation.
  5. Then all the methods of the object, which are just functions specific to object type itself.

class Node {
  // Instance variables here.
  float x;
  float y;
  float radius;

  // The "constructor" method. Used to initialize
  // the object when it is created. There always
  // must be a constructor; it always has the
  // same name as the object itself, and it
  // never has a return type, not even "void."
  // They don't require arguments.
  Node( float x, float y ){
    this.x = x;
    this.y = y;
    this.radius = 5;
  }

  // The object's methods.
  void draw(){
    strokeWeight(2);
    stroke(0);
    fill(128);
    ellipse( this.x, this.y, this.radius*2, this.radius*2 );
    strokeWeight(1);
  }
}

“this” Keyword

Notice the keyword “this” in the class definition. This ensures that we are referring to the instance variable of the class and not some other variable with the same name, like global variables declared outside the class, function arguments, or other temporary variables declared inside the function.

“this” means to refer to the object in which it is used. The keyword has no meaning outside of an object. (However, Processing itself wraps all of your code with a class definition when pressing Play. You’ve been writing a class definition every time you’ve written a sketch. So be careful with “this.”)

Creating an “Instance” of a Class


// Declare a variable to contain the object.
Node myNode;

// Call the constructor to create the object.
myNode = new Node( 15, 25 );

Next comes object instantiation.

The creation of an instance of a class is a two-step process. It is related to the process of creating regular variables, but the steps are specific to objects and their needs.

When variables are declared, they must then be initialized with a default value, some value that the variable will contain at the start. A similar process happens with objects.

First, declare a variable to contain the object using the name of the class. This is the same syntax as normal variables, which makes sense when considering classes as user-defined types.

Next, call the constructor function to instantiate the instance. This always uses the “new” keyword to tell Processing that we’re dealing with an object, and it needs to create a new one.

Using Objects (Dot Notation)

We “access” instance variables and “call” methods.

Dot notation is a universal kind of notation used in most every object-oriented programming language, including C++, Java, Python, and Ruby.

We can use this notation to “set” and “get” the values of instance variables, and to “call” an object’s methods.

Dot Notation Syntax


// Accessing an instance variable.
println( myNode.x );

// Change the position of a node;
myNode.x = 100;
myNode.y = 120;

// Draw the node by calling the draw() method.
myNode.draw();

Accessing instance variables is easy. Just type the name of the variable, then a period, then the instance variable name.


myObject.instanceVariable = value; // Setting.
println( myObject.instanceVariable ); // Getting.

Calling methods is just as easy, just type the name of the variable, a period, then the method as if you were writing any other function.


myObject.methodName(); // Calling.

Multiplicity with Objects

Using arrays and objects adds a step to instantiation, but it’s really the same as regular variables.

Arrays and objects together are a good fit for creating complexity from simple behaviors, and for writing generative algorithms that simulate growth.

Once we get a handle on arrays of objects, we’ll tackle ArrayLists, which are more flexible way of dealing with objects of different types.

Process


// Declare the array.
Node[] nodes;

// Size the array with objects.
nodes = new Node[ 100 ];

// Instantiate all the objects.
for( int i=0; i < nodes.length; i++ )
  nodes[i] = new Node( 0, 0 );

// Draw all of the nodes with this.
for( int i=0; i < nodes.length; i++ )
  nodes[i].draw();

Declare the array and initialize the empty array like you normally do with other data types (like float)by using the new keyword and telling Processing how many objects you need in the array.

Then use a for-loop to instantiate each object by calling the constructor.

Manipulating Arrays of Objects

Elements can be added to or subtracted from arrays.

Arrays aren’t always static. You can add elements and subtract them, and Processing gives us some methods to do so with ease.

The raw process of adding an element to an array in Java involves creating a completely new array with one more empty element, copying the original array into the new array, adding the new element into the blank slot, then deleting the old array.

Syntax

The syntax to the right adds another technique called “typecasting,” where there are a set of parentheses to the left of the append() and shorted() methods. Type-casting is necessary when a generic function doesn’t know what class type is contained in the array, and we therefore must be explicit as to the type that should be returned.

See Also

Check the Processing reference for the expand(), splice(), subset(), concat(), etc. under “Array Functions.”


// Let's say we have an array of Nodes.
Node[] nodes = new Node[ 100 ];
for( int i=0; i < nodes.length; i++ )
  nodes[i] = new Node( 0, 0 );

// We instantiate a new node.
Node nodeToAdd = new Node( 100, 100 );

// Append it to the array. This adds it to the
// end of the array as the last element.
nodes = (Node[]) append( nodes, nodeToAdd );

// Shorten the array by deleting the last element.
nodes = (Node[]) shorten( nodes );

Advanced: Inheritance

Nodes with collision detection. Check the source code.

Classes with more specific behavior can be created from existing classes.

Inheritance is one of the fundamental concepts of object-oriented programming. It allows us to build up functionality from a basic level to more advanced and specific levels without rewriting code.

For instance, we may have a generic class Geometry, then extend the class to Point, Line, Curve, Surface, which can be further extended to NurbsSurface. A NurbsSurface is still a Geometry object, but it has more instance variables and methods that determine its specific characteristics.

Derivation Syntax

Creating a derived class, one that extends the functionality of an existing class, is a matter of using the “extends” keyword.


class CollidingNode extends Node {
  CollidingNode( float x, float y, float r ){
    super( x, y );
    this.radius = r;
  }

  void checkCollisions(CollidingNode[] otherNodes){
    for( int i=0; i < otherNodes.length; i++ ){
      // Nodes can't collide with themselves.
      if( otherNodes[i] != this ){
        float distance = dist( this.x, this.y, 
          otherNodes[i].x, otherNodes[i].y );

        float allowed = this.radius + 
          otherNodes[i].radius;

        if( distance < allowed ){
          // We're colliding with this node. 
          // Move the other node.
          float overlap = allowed - distance;
          float deltaX = otherNodes[i].x - this.x;
          float deltaY = otherNodes[i].y - this.y;
          float ratio = overlap / distance;

          otherNodes[i].x += deltaX*ratio;
          otherNodes[i].y += deltaY*ratio;
        }
      }
    }
  }
}

Polymorphism

Polymorphism is the ability to treat derived classes like their parents.

Polymorphism parallels biological taxonomy. A cheetah is a carnivore, but also a mammal, and is also an animal.

Polymorphism

Links between nodes can connect to Node or CollidingNode classes.

This following class “links” nodes together, just by drawing a line between two of them.


class Link {
  Node a;
  Node b;

  Link( Node a, Node b ){
    this.a = a;
    this.b = b;
  }

  void draw(){
    strokeWeight(2);
    stroke( 255, 0, 0 );
    noFill();
    line( a.x, a.y, b.x, b.y );
    strokeWeight(1);
  }
}

Because the constructor method accepts a Node, we can give it a CollidingNode, a derived class of Node, because a CollidingNode is also a Node.

This works because since we write code just for a Node, we only reference what all the derived classes have in common, all the functionality from the base class Node.

Derived classes can never take away from the base class, but they can redefine methods.

Potential

The potential of object-oriented programming translates directly to architecture. If we create systems of objects that represent building types, components, organizations, etc., it stands to reason that we can combine them in meaningful ways as a model for architectural design.

Building Information Modeling

BIM is the first step in this transformation from a medium-based design paradigm to a model-based design paradigm. The fact that we can imbue a model with rules, constraints, problem-specific criteria, evaluations, self-generating behaviors, will change the way we approach design.

We will start describing the problem at a high level, on the level of systems, goals, unknowns, and problems, then develop a model that harnesses and addresses those constraints. We will evolve the model and let it grow more sophisticated, carrying our decisions to greater levels of specificity, all guided by the first principles, goals, and systems laid down at the outset.

An understanding of how to use data is absolutely necessary to take full advantage of the potential of computation.