Jump to content

Object Oriented Programming/Printable version

From Wikibooks, open books for an open world


Object Oriented Programming

The current, editable version of this book is available in Wikibooks, the open-content textbooks collection, at
https://en.wikibooks.org/wiki/Object_Oriented_Programming

Permission is granted to copy, distribute, and/or modify this document under the terms of the Creative Commons Attribution-ShareAlike 3.0 License.

Introduction To OOP

Introduction

[edit | edit source]

For an overview and history of Object Oriented programming OOP, please reference the Wikipedia article.

The reader is expected to have a basic familiarity with programming in general, as we will give examples in a variety of languages. We will explain any non-obvious syntax in the discussion, although this is beside the point. The point is to give some indication of the flavor of the languages and some insight into the real-world application of OO ideas.

Overview

[edit | edit source]

We will divide up OOP into two phases— classic and modern. While this distinction is somewhat arbitrary, we believe it is instructive to consider OOP as it was practiced in the 1980s and early 1990s to demonstrate the motivation for more current practices.

What is Classic OOP?

Object oriented programming can be traced back to a language called Simula, and in particular Simula 67, which was popular during the 1960s. It was Simula that first instituted "classes" and "objects," leading to the term "object oriented" programming. By the early 1990s, enough experience had been gained with large OOP projects to discover some limitations. Languages such as Self, ideas like interface programming (also known as component or component-oriented programming), and methodologies such as generic programming were being developed in response to these difficulties. Although often derided by OOP purists, it was the standardization of C++ in 1998 —including generic programming facilities — that really ushered in the modern era of OOP, which we also refer to as Multi-Paradigm programming. This is largely due to the popularity of C++ and the genius of the Standard Template Library (STL) demonstrating the utility of the new methodology to such a large audience.

Classic or Pure OOP

[edit | edit source]

By 1980, Xerox had made Smalltalk available to outsiders, appropriately named Smalltalk-80. Unlike other early programming languages, Smalltalk was a complete environment rather than just a language, a characteristic it had in common with Lisp at the time. While Lisp machines were foreshadowing IDEs to come, Smalltalk was pioneering the GUI, ultimately influencing the development of the Macintosh computer. Meanwhile, while Xerox was developing Smalltalk during the 70s, the C language was becoming popular thanks to UNIX being largely written in C. Therefore, it was C —an otherwise unlikely candidate —that Bjarne Stroustrup fused with ideas from Simula to create "C with Classes" which was renamed to C++ in 1983. In 1985, unsatisfied with Smalltalk, C++, and various object systems being tacked onto Lisp dialects, Bertrand Meyer created Eiffel. Even though much more recent, Java essentially cloned vintage C++, and so we consider Smalltalk, vintage C++, Eiffel, and Java to be Classic OOP. The object systems tacked onto Lisp (eventually standardized as CLOS in 1994) produced a very different methodology. While we don't consider CLOS to be Classic OOP, it did influence modern OOP.

Modern OOP and Multi Paradigm Programming

[edit | edit source]

Classic OOP developed a tendency to over-rely on a technique called "inheritance," and eventually programmers realized that they were using inheritance for many things that are conceptually distinct. Modern OOP basically incorporates these concepts, sometimes as language level features, sometimes through programmer practice. The goal largely being looser coupling, easier maintenance and reuse. Historically, David Ungar and Randall Smith completed their first working Self compiler in 1987, and by 1990 Sun Microsystems had picked up the project. While Self did not survive to really become a modern OOP language, it was a second generation OOP language. Then, in the early 1990s, Alexander Stepanov and Meng Lee pioneered generic programming and wrote early drafts of C++'s STL. This began a (still ongoing) trend of incorporating functional programming ideas into more traditional OOP environments, in a kind of reverse CLOS. Additionally, the early 1990s saw the development of CORBA and Microsoft's COM, which was a natural extension of the ideas that had led to the original Windows API. This interface or component programming was a natural extension of encapsulation —a basic tenet of OOP, as we will see. All of these developments were aimed at further managing or reducing complexity. Since this was the original goal of OOP and these techniques are used in conjunction with classic OOP, we find it appropriate to consider them in a treatment on "OOP".

Contemporary object oriented programming, therefore, tends to be rather distinct from classic object oriented programming. Particularly, getting used to which abstractions are the most useful for which problem types is more challenging now that there are more from which to choose! In a now classic book, Gamma et. al. introduced Design Patterns which helped to synthesize a variety of OOP techniques as applied to very common problems.

Future of OOP

[edit | edit source]

The future contains more standardization of functional programming techniques in OOP environments, particularly lambda expressions and closures, and more robust meta-programming constructs. Applying design patterns automatically through generic or meta programming techniques is an interesting area.

What is an Object?

[edit | edit source]

Loosely, the term object is used to conjure up connections with real world objects like a chair or a guitar. Except that for software, only some simplified abstraction is used, designed specifically for the task at hand. While a real chair is composed of atoms and molecules and reacts to its environment based on the laws of physics and its atomic composition, a "chair object" will vary wildly depending on whether you're writing a game or a Point-of-Sale system for a furniture store. Approaching your problem from the perspective of the chair will not be as productive as approaching the chair from the perspective of your problem.

In most of the languages used in this book, you will find that technically speaking (something akin to whistling 9600 baud, I understand), an object is "an instance of a class". Great, so what does that mean? Well, we can trace this idea all the way back to Plato and his Platonic Ideal. If you spent more time watching Bill and Ted's Excellent Adventure than reading Plato, the idea is that the concept of a chair is a separate entity from any particular chair. In other words, you can conceive of a chair in the abstract without thinking of any particular chair.

In most OOP languages, this abstracted idea of a chair is called a class (from classification) and is a prototype or blueprint for actually making chairs. The act of making something from the blueprint is often called instantiating, and the made thing is both an object and an instance of the class that served as a blueprint. As humans, we normally tend to do this in reverse —we categorize objects we encounter. We can easily identify chair-like things we run into as being chairs; this classification is where we got the term class in the first place.

It is easy to drift off into abstruse philosophical debates over objectness; in some areas like knowledge representation and computational ontology they are very relevant. However, for a computer programmer, it is only necessary to figure out what your application needs to know about and do with chairs. This can be a sufficiently difficult problem, it's usually not necessary to make it harder!

If you're still fuzzy on the whole idea, consider a more technical explanation. Structs (structures), records, tables, and other ways of organizing related information predated object oriented programming. You may be familiar with something like the following Pascal code:

 TYPE chair = RECORD
                model  : integer;
                weight : integer;
                height : integer;
                color  : COLOR;
              END;

This doesn't actually create a chair variable, but defines what a chair variable will look like when you create one. You could proceed to create arrays of chairs and so forth, and as we hope you've discovered for yourself, this kind of thing is quite indispensable for keeping your programs understandable. Object oriented programming wants to push this advantage and milk it for every ounce of understandability, correctness, and simplicity it can.

A fundamental problem solving technique is divide and conquer —you just have to figure out how to partition your problem into sub-problems. OOP innovators realized that we already had figured out ways to partition the problem, and it was reflected in the way we organized our data, like above. If you looked at an application that had that chair RECORD in it, you would surely find lots of code for doing things with chairs. Why else would you bother to define it? So, if you were to extract all of that code from all over the application and put it together with the chair definition, the reasoning goes, you should have an easier time ensuring that:

  • all chair code is correct
  • all chair code is consistent with each other
  • there's no duplicated chair code
  • overall, you have less spaghetti because chair code is no longer tangled up with sofa code etc

So you take that chair definition and that code extracted from all over the application, and call that a class. Take a chair variable and call it an object, and you're off to the races.

Since this is supposed to be a pragmatic book, let's look at our first example code, this time in Python code:

 class Chair:
    model = None
    height = None
    weight = None
    color = None
    def has_arms(self):
        return self.model % 2 # odd-numbered models have armrests

This doesn't look terribly different from the Pascal example. The only difference is that class Chair now contains a has_arms method. Methods are functions that live within a class, and are usually used to process certain class specific data. Hopefully the purpose is fairly clear, so let me point out the important part: has_arms is a calculated or inferred property—this information is not stored directly.

In Python, you would use this class like so:

 c = Chair()
 c.model = 15
 c.height = 40
 c.weight = 10
 c.color  = 7

 if c.has_arms():
      do_something
 else:
      do_other_thing

Here, the "c" variable is an instance of the 'chair' Class, also known as a chair object. We initialize the properties like you would in Pascal (this will change later!) and do one thing if the chair has arms and something else if it doesn't. But we never initialized a "has_arms" property or anything of the like.

Since part of the goal is for you to become syntax agnostic, we present this same example again in C++:

 class Chair
 {
 public:
     int model;
     int weight;
     int height;
     int color;
     bool has_arms()
     {
         return model % 2 ? true : false;
     }
 };
 Chair c;
 c.model = 15;
 c.height = 40;
 c.weight = 10;
 c.color  = 7;

 if (c.has_arms())
      do_something();
 else
      do_other_thing();

Now, we would just like to mention that this is just the tip of the iceberg and this example is not representative of good style (in either language). There may seem like little purpose to making objects with so little functionality, and you could easily generate equivalent code that requires no new class:

struct Chair
{
    int model;
    int weight;
    int height;
    int color;
} c;

Chair c = {15, 40, 10, 7 };

if (c.model % 2)
     do_something();
else
     do_other_thing();

The purpose of this section is to help you understand the terms; we will delve more into the benefits in the sections on encapsulation, polymorphism, inheritance, and on and on. However, let us say that while the "low-level" way might seem shorter and simpler, the object oriented benefits accrue with program size and complexity. This is not surprising, as that's what OOP was designed for, but it does make simple examples hard to come by. So bear with us.


References

[edit | edit source]


Classes

In Object-Oriented Programming (OOP), a class is a sort of blueprint for creating objects. It defines a new data type that encapsulates data and behavior (methods) related to a particular entity or concept. Classes can serve as templates, allowing us to create multiple objects with similar characteristics and behaviors. Classes are essentially a giant container for a bunch of functions that can either rely on each other or independently operate within the class.

Components of a Class

[edit | edit source]

A class consists of two primary components: attributes (also known as properties or data members) and methods (also known as member functions).


1. Attributes: Attributes are variables that represent the state of an object. They store information about the object's characteristics. In the animal analogy, attributes could include the species, name, age, weight, color, etc. Attributes define what data an object can hold.


2. Methods: Methods are functions that define the behavior of the object. They represent the actions an object can perform. In the animal analogy, methods could include "make_sound()", "move_type()", "eat_what()", etc. Methods define what operations an object can perform.


Most classes are made on their own python files and to be used or accessed by other python files. Being a class on its own page allows other python files to call and reference the class without directly affecting the code in the class. Classes can be used for calculating equations, registering user input and acting with it, and raising exceptions to other python files with, again, not being directly affected by other python files' codes.

Subclasses are essentially a child of a Class. Think of Classes as Parents, and Subclasses as Children. Parent Classes can pass properties or functions onto their Child classes. Or think of it as a Child inherits things from their Parent. Subclasses are for more complex programs that utilize multiple ideas under one grand idea.

Below is an template of creating a class in Python (or most other OOP languages), we use the class keyword followed by the class name and a colon. Within the class, we define attributes and methods.

class MyClass:
    # Constructor method (__init__) initializes object attributes
    def __init__(self, attribute1, attribute2):
        self.attribute1 = attribute1
        self.attribute2 = attribute2

    # Method 1
    def method1(self):
        # Method code here

    # Method 2
    def method2(self):
        # Method code here

Here is an real example in Python of creating Animal class blueprint. This is a blueprint for all animals , called the "Animal" class, and it will contain common attributes and behaviors shared by all animals in the zoo. In this class, we've defined attributes such as "species," "name," and "age." We also have two methods: "make_sound()" , "move_type()",eat_what(). The methods are empty for now (marked with pass) as each animal will implement them differently.

class Animal:
    def __init__(self, species, name, age):
        self.species = species
        self.name = name
        self.age = age

    def make_sound(self):
        pass

    def move(self):
        pass
Zebra Biblical Zoo 01
Zebra Biblical Zoo 01

Now, we shall make a individual class Zebra as followed:

class Zebra(Animal):
    def __init__(self, name, age):
        super().__init__("Zebra", name, age)

    def make_sound(self):
        return "Bray"

    def move(self):
        return "Galloping."

As you can see In those classes, we use inheritance to inherit the attributes and methods from the Animal class. We then override the "make_sound()" and "move()" methods to implement the specific sound and movement behavior for each animal.

Now , we already create a Zebra class animals and we can now create individual animal objects and interact with them in the main program.

def main():
    # Create animal objects
    zebra1 = Zebra("Marty", 10)

    # Interact with other  animals
    print({zebra1.name} " the " {zebra1.species} " says: " {zebra1.make_sound()})
    #Marty the Zebra says : bray
    
    #Interact with enviroments
    print({zebra1.name} " the " {zebra1.species} " aged " {zebra1.age} {zebra1.move()})
    #Marty the Zebra aged 10 galloping
    
if __name__ == "__main__":
    main()

See also

[edit | edit source]

YouTube Tutorials

[edit | edit source]


Properties

Properties in OOP are also able to be looked at as functions, a property houses a function that can have its procedures or variables altered without directly going to the code and editing it. A property can be changed or updated based on user input which allows for a lot of user-interactive programs and applications.'

In Python, the property keyword allows you to define getter, setter, and deleter methods for class attributes, making them act like properties. This enables you to control the access and modification of class attributes while providing a clean interface for external code. In example , we use the @property decorator to create getter methods for each attribute, allowing us to access these attributes using obj.attribute1

class MyClass:
    def __init__(self, attribute1, attribute2):
        self._attribute1 = attribute1
        self._attribute2 = attribute2
        
    @property
    def attribute1(self):
        return self._attribute1

    @attribute1.setter
    def attribute1(self, value):
        # Perform any validation or custom logic here
        self._attribute1 = value
        
# Usage example
obj = MyClass("Value 1", "Value 2")
print("Attribute 1:", obj.attribute1)

#Using property to add in new values
obj.attribute1 = "New Value 1"
print("Updated Attribute 1:", obj.attribute1)


sss

class Animal:
    def __init__(self, species, name, age):
        self._species = species
        self._name = name
        self._age = age

    # Getter methods
    @property
    def species(self):
        return self._species

    @property
    def name(self):
        return self._name

    @property
    def age(self):
        return self._age

    # Setter methods
    @name.setter
    def name(self, new_name):
        self._name = new_name

    @age.setter
    def age(self, new_age):
        self._age = new_age

    # Other methods
    def make_sound(self):
        pass

    def move(self):
        pass

See also

[edit | edit source]


Methods

Class Methods

[edit | edit source]

If you are familiar with regular functional programming, then you are well acquainted with functions. For those who do not know(or forgot), a function is a block of code that executes a specific task when called by the program. Class methods are nearly identical to functions in practical use, but there are a few key differences. A method is a function that belongs to its class. Methods, assuming that they are dynamic and not static, can access properties as well as other methods that belong to its class. Methods can also be called manually by the user of the class, assuming it's a public and not a private method, once it has been instantiated. Below is an example of what the use of class methods can look like.

Example 1

[edit | edit source]

Class.py

[edit | edit source]
class Values():

  def __init__(self):
    pass

  def squared(self, value):
    return = self.value * self.value

Main.py

[edit | edit source]
from Class.py import Values

instance = Values()
print(instance.squared(4))

# Output: 16

In the example above, a method called squared() will take the value of self.value and multiply it by itself. Here, the method is public so it is able to be called by Main.py.

Example 2

[edit | edit source]

Class.py

[edit | edit source]
class Values():

  def __init__(self):
    pass

  def squared(self):
    return = self.add_ten(self.value * self.value)

  def add_ten(self, value):
    return value += 10

Main.py

[edit | edit source]
from Class.py import Values

instance = Values()
print(instance.squared(4))

# Output: 26

In this example, we call the method squared() but then that method calls another method add_ten(). Just like functions, methods can be called within other methods. Now, you should be able to see the functionality of methods inside of their classes.

See also

[edit | edit source]

YouTube tutorials

[edit | edit source]


Constructors

What is a constructor?

[edit | edit source]

In Object Oriented Programming, a constructor is a function that is executed when a new class object is created. This subroutine ensures that the class is properly instantiated. The constructor first checks to make sure that there are enough resources(memory) available to create the new object and then allocates the memory. Afterwards, the constructor can execute the custom code that can be optionally provided by the programmer. This is useful if each class has passed in data when created, making each class unique. You can use the constructor to assign the passed in parameters to specific properties within the class, while possibly calling other class methods for data manipulation within the constructor if needed. It's important to note that constructors are only called once per object, so once a class has been instantiated, the constructor will not be used again for that particular instance of the class.

Example

[edit | edit source]
Class.py
class Values():

  def __init__(self, value):
    self.value = squared(value)

  def squared(self, value):
    self.value = self.value * self.value
Main.py
from Class.py import Values

instance = Values(4)
print(instance.value())

# Output: 16

From the example above, you can see that we have an __init__ function. In Python, this is the programmer controlled aspect of the class constructor. This example shows that the constructor takes the passed in value, and squares it. This is all done when the class is instantiated with no methods being directly called by Main.py. Constructors are very handy in situations when there are passed-in values, when the class is instantiated, as well as when those passed in values have to be manipulated before use.

See also

[edit | edit source]


Lifetime Management

Lifetime Management

[edit | edit source]

Object Lifetime Management is the concept of controlling the number of instances a configured service will have and the duration of the lifetime of those instances. In other words, it allows you to determine how returned instances are cached.

Manual Lifetime Management

[edit | edit source]

Factories

[edit | edit source]

In the real world, factories are used to make stuff (e.g. automobiles). In OO programming, factories also make stuff; specifically, OO factories "make" (instantiate) objects.

At least in Java, a constructor is unable to return a reference to an existing object—it MUST return a brand-new object. This can cause two problems—too many object instances, and identical objects that are duplicates—not the exact same object.

Factories are generally implemented as a public static method inside a class that has only private constructors. In this way, only the factory can create instances "Guaranteeing[1]" that all instances of the object have been created by the factory.

The factory most often has an internal "Cache" of objects. Whenever a user asks the factory for a new object, the cache is consulted first and one of the cached objects are returned if it is identical—otherwise a new instance is created, added to the cache and returned.

Factories can also be used to inject user code into an "Unmodifiable" library. It is possible to save and restore a user object that implements some important functions of a library object. The library then uses that factory to get instances of the users object, and doesn't even know that the user has replaced a core component with completely new code.

As an example... Say the library uses Library objects all over the place. The library user can create a class (let's call it User) that extends Library. The user then calls LibraryFactory.set(User.class), and the factory stores it. Inside the library, whenever the library wishes to use a Library object, it calls the LibraryFactory.Factory() function to get a new instance. Without changing any code, the user can now completely change the way the library functions by overriding one or more methods in his User class.

This is used in various places inside the Java SDK to allow you to change how chunks of the SDK works without rewriting any of the code.

  1. In Java, just having private constructors does not truly guarantee that all your objects came from the factory. Serialization and reflection can still create new instances without going through the constructor.

Reference Counting

[edit | edit source]

Reference counting is a programming technique of storing the number of references, pointers, or handles to a resource, such as an object, a block of memory, disk space, and others.

Garbage Collection

[edit | edit source]

Garbage collection refers to freeing of heap-allocated memory. The heap memory must be freed eventually. Either the programmers code must explicitly free it, or some automatic system mechanism is required.

The most common mechanism is mark and sweep. This is where the system knows of certain top-level classes (Threads and windows mostly). It follows all the references from these "Known" classes to every class that can be reached and marks each class. When it's done, it "sweeps" (gets rid of) every unmarked class.

Garbage Collection is a huge enabler of Object Oriented development—it completely changes the way you design. With code-based memory management it is necessary to have some way to track an object. If it is created on the stack, it will be deleted when the method exits (so instances passed outside your method will become invalid). If it is created on the heap, some object must delete it manually—so if your method is going to create an object and pass it to two other objects that both retain the object, you have a problem. Who deletes the object? The original method can't because it will have exited before the two objects are finished with it, and the two objects shouldn't know anything about each other.

This type of problem is often solved with "Reference counting" which is another form of garbage collection, but a manual one. It also leads to fewer long-lived, free-roaming classes being created since they have to be tracked.

GC solves problem, that of ownership. If you are programming in a language with Garbage Collection, you may create classes on the fly, pass them off, retain a copy or forget about it... You really can't go wrong. When all instances have been "Forgotten", the class disappears.

The drawback is that GC tends to take a solid chunk of time. This is less true lately as at least Java has threaded GC and the ability to limit the amount of time GC will run. There are actually dozens of tuning and reporting options and the ability to select from multiple GC systems to fit your need.

GC Implementations run from trivial (instance counting) to amazingly complex. The current default Java VM uses a multi-tiered system with different mechanisms for new, middle-aged and old objects. For instance, since almost all new objects tend to die almost instantly, the first section (Eden) is divided in half. When one half becomes full, the currently referenced objects are copied to the other side. The system switches to using the "new" side and completely forgets about the old half. There is no explicit code invoked to delete objects that are already gone.

This means that GC can actually be more efficient than traditional allocation/free since there is no free operation for nearly all objects.

Garbage collection should be considered essential to OO Design.

Persistence

[edit | edit source]

Persistence is simply the act of having objects that "Stick around" between invocations of your program.

This can be trivial (Just serializing an object out when exiting, serializing it back in when loading) to a full OO-database implementation.

Hibernate is a good program that sits between your application and a database and ensures that your objects are properly persisted.

Serialization

[edit | edit source]

Serialization is converting an object to a stream of bytes that can then be saved to a file or transmitted over a network.



Getters and Setters

Getters and Setters (also known as accessor and mutator methods respectively) are two types of methods used to control the access and modification of class attributes (also called properties or member variables).

Getter

[edit | edit source]

A getter method is used to retrieve the value of a private or protected class attribute. It provides read-only access to the attribute, allowing other parts of the program to retrieve its value without directly accessing the attribute. By using a getter, you can control how the attribute is accessed and apply any necessary logic or validation before returning the value.


In most programming languages, the naming convention for a getter method is to prefix it with "get," followed by the attribute name. For example, if you have a private attribute age, the corresponding getter method would typically be named getAge().

class Person:

    def __init__(self, name, age):

        self._name = name

        self._age = age

    def getAge(self):

        return self._age

# Usage

person = Person("John Doe", 25)

print(person.getAge())  # Output: 25

Setter

[edit | edit source]

A setter method is used to modify the value of a private or protected class attribute. It provides a way to update the attribute while enforcing any necessary constraints or validation rules before making the change. By using a setter, you can control how the attribute is modified and prevent any inappropriate changes.

In most programming languages, the naming convention for a setter method is to prefix it with the word "set," followed by the attribute name. For example, if you have a private attribute age, the corresponding setter method would typically be named setAge().

class Person:
    def __init__(self, name, age):
        self._name = name
        self._age = age

    def setAge(self, new_age):
        if new_age >= 0:
            self._age = new_age

# Usage
person = Person("John", 25)
person.setAge(30)
print(person.getAge())  # Output: 30

person.setAge(-5)  # The age will not be updated because of the validation in the setter method
print(person.getAge())  # Output: 30 (age remains unchanged)

See also

[edit | edit source]


Static vs Dynamic

Static vs Dynamic

[edit | edit source]

In Object-Oriented-Programming languages, exists static and dynamic methods as well as properties. Here are the key differences.

Static

[edit | edit source]
  • Cannot access dynamic methods of its own or other class's
  • Cannot access dynamic properties of its own or of another class's
  • Are only instantiated once since they are static and separate instances will always be identical

Dynamic

[edit | edit source]
  • Can access dynamic methods of its own or other class's
  • Can access dynamic properties of its own or of another class's
  • Can be instantiated multiple times, each time with its own unique instance working with potentially unique data

Example

[edit | edit source]
class Values():

  def __init__(self, value):
    self.value = value

  def squared(self, value):
    self.value = self.multiply_value(value, value)

  @staticmethod
  def multiply_value(value1, value2):
    return value1 * value2

In the code above, you can see that the method squared() takes self as a parameter. This allows it to access other properties and methods of the class. As you can see, it takes an initial passed in value, does some modification to it, and then sets the class's value property to the result of the calculation. Since it can access other class methods/properties and modify the class's state, it is considered a dynamic method.

On the other hand, you can see that multiply_value() does NOT have self as a parameter. All that this method does is multiply the two passed in values together and return the result. It never has or requires access to its class's properties or methods, it just does the calculation. Since it doesn't need to access unique data of its class, it is only loaded once as any other instance would be identical and a waste of memory, making this a static method.

See also

[edit | edit source]


Private vs Public

Private vs. Public

See also

[edit | edit source]


Encapsulation

Encapsulation

In-the oriented approach, classes,and therefore also objects of that class,are similar to black boxes with a clearly defined interface. This interface is the only mechanism that other objects can use to communicate with the object

We say that inside a class, behind the interface, the data and implementation of the operations are encapsulated or enclosed in a capsule.

This makes the internal workings of a class invisible to all other classes. This is a good technique, since there is no need for other classes to know the internal structure of this class,in other words, how it represents its data and how it performs its operations. We call this encapsulation information hiding.

See also

[edit | edit source]


Inheritance

Inheritance

[edit | edit source]

In many books, inheritance and OOP are made to seem synonymous, so it may seem strange that we deferred this discussion so far. This is a reflection of the diminished role of inheritance over time. In fact, one of the primary distinctions between Classic and Modern OOP lies in the usage of inheritance. As the old adage goes, if all you have is a hammer, then everything looks like a nail. And so it happened that often times, inheritance was the only tool available to the erstwhile OOP programmer, and so every concept under the sun (or at least the ghostly glow of a CRT) was crammed into inheritance. This lack of conceptual integrity and separation of concerns led to over-intimate dependencies and many difficulties. In some languages, programmer technique evolved to make the concepts clearer using the same limited language functions, while other languages explicitly developed features to address these concerns. Because you are almost certain to be exposed to some of this misguided advice at some point in your OOP learning, we'll try and explain some of the problems to you. However, much of the discussion will go in the sections where they properly belong!

First off, though, what is inheritance? Well, just like when your grandma dies and you inherit that gawd-awful ugly lamp, inheritance in OOP grants the child-class (or derived-class) all of the attributes of the parent (or super) class. So, for example (in C++):

class Parent
{
public:
   int f() {return 10;}
};
class Child : public Parent
{
// see, nothing here!  But wait, we inherited the function f()!
};
Child c;
int result = c.f();  // huh?  Oh, f() was inherited from the parent!

Is-A vs Has-A Moley Baloney

[edit | edit source]

The most over-used and rather worthless discussion on inheritance that you will see revolves around the "Is-A vs Has-A" discussion. For example, a car is-a vehicle but has-a steering wheel. The idea these authors are chasing is that your car class should inherit your vehicle class and have a steering wheel as a member. You might also run across examples with shapes, where a Rectangle is-a Shape. The point is, once again, abstraction. The goal is to identify operations that operate only on the abstract notion of vehicle or shape and then write that code only once. Then, through the magic of inheritance, you can pass in cars or rectangles or what-have-you to the generic code, and it will work, since the derived classes are everything the parent classes are, "plus more".

The problem here is that inheritance is mixing together several things: you inherit "typeness", interface, and implementation all at the same time. However, all of the examples focus on interface while talking about "typeness". The abstract code doesn't care that a car "is-a" vehicle, just that the objects respond to a certain set of functions, or interface. In fact, if you want to give your chair class accelerate(), brake(), turn_left() and turn_right() methods, shouldn't the abstract code be able to work on chairs then? Well, of course, but that doesn't make chairs vehicles.

The kinds of solutions proposed in these "is-a" discussions have thus been mostly replaced by so-called interface programming and template programming. Since template programming offers the loosest coupling, it focused some attention on the syntactic-semantic confusion. Just because you have functions with the proper names, does that mean that they do what you think they do? If a chair class has accelerate(), brake() and other vehicle type methods, does it make sense to have generic vehicle code work on this chair? This led to more codifying of the assumptions of generic code: say, brake(INFINITY) ==> STOPPED. This implies that brake(x) != accelerate(-x). So chair may make a better vehicle than the Starship Enterprise!

Delegation and "Dynamic Inheritance"

[edit | edit source]

You will see more about delegation later in the section on message passing, but we wanted to mention that many problems solved by delegation are often tackled by inheritance in languages that don't offer a delegation feature. It's easier to show an example than explain, so in short, here's what delegation looks like:

interface I
{
   int f();
}
class A implements I
{
   // A member variable that actually services all requests for
   //  calls to the I interface.
   delegate I private_i;

   A(I target)
   {
       private_i = target;
   }
}
class B implements I
{
   int f() { return 1; }
}
B b;  // create a B object to be the target of A's delegation
A a(b);  // Create an A object, passing in b

int foo = a.f();  // This call to f() is delegated to b.f(), causing 1 to be returned.

Inheritance accomplishes a similar feat

class B
{
   int f() { return 1; }
}
class A inherits B
{
}
A a;

int foo = a.f();  // returns 1 just like in the delegation example.

However, the delegate can change over time, whereas base classes cannot. As a result, sometimes delegation is referred to as "dynamic inheritance".

Multiple Inheritance

[edit | edit source]

Multiple inheritance is where an object inherits its properties from many different classes. For example, a house is both a "building" and a "place to sleep in".

In multiple inheritance, an object inherits its properties from many objects (its parents or base classes). Therefore, we need to set up some classes, which we will use C++ for:

class Building
{
  int size;
  int purpose;
  int price;
};
class PlaceToSleepIn
{
  int type; // tent, house, dorm, etc.
  bool pleasant_to_live_in; // true or false
};

In C++, to inherit all the types, one uses the : operator, like so:

class House : public Building, public PlaceToSleepIn
{
  // all of the Building and PlaceToSleepIn variables are here!
  int rooms; // the number of rooms
};

Problems with Inheritance

[edit | edit source]

One problem of Inheritance is that users of predefined classes may not know the location of the abstracted data. Therefore, it may not be easy for them to obtain it. Most of the problems with inheritance is due to design fragility or issues with the way a programming language implements it.

A normal issue in dealing with inheritance in large programs is called Fragile Base Class. It arises when subclasses of the base class make assumptions about the property (the definition of the attribute) that are outside the contract provided by the base class.

An example could be of an attribute that was declared in the base class as byte but later turns out to need the range of word. Changing this to word will necessitate at least recompiling all sub-classes but that might well not be enough because the sub-classes might have relied on the type of this attribute so that the source code also will need to be changed.

This intrinsic knowledge of the implementor about a given implementation is a problem in general programming but one that OOP/OOD does attempt to solve by offering greater opportunities for better programming practices. But as anything else with code it is up for the programmer to make use of it as to be as transparent as possible as to enable other to take full advantage of its code and so of the property of inheritance.


Subclasses

Subclasses, also referred to as derived classes, heir classes, or child classes, are classes that inherit one or more language entities from another class/classes. While their may be some minor differences in how class inheritance works between different languages, generally, the subclass automatically inherits the instance variables and member functions of it's superclasses or parent classes. [1] From the wikipedia page on inheritance, you can see how a subclass is generally defined. [2]

class SubClass: visibility SuperClass
{
    // subclass members
};

The colon at the end of 'Subclass' means that it inherits from the "SuperClass" at the end.

Non-subclassable classes

[edit | edit source]

Some languages have non-subclassable classes. These are classes that have no subclasses. Non-subclassable classes can be created by adding certain class modifiers to the class declaration. After being created the non-subclassable classes will restrict reusability of code, especially when source code is not accessiable. A few examples of class modifiers that creat Non-subclassable classes include Java's 'final' keyword or C#'s 'sealed' keyword. These modifiers are added to the class declaration before the class keyword and identifier declaration.

Since a non-subclassable class has no subclasses, references or pointers to objects of that class can be easily deduced to be referencing instances of that class and not instances of subclasses or superclasses since a non-subclassable class has no subclasses, and an instance to a superclass would be violating the type system. Early binding(static dispatch) can also be used instead of late binding(dynamic dispatch) due to the exact type of the object referenced being known before execution.

References

[edit | edit source]
  1. https://en.wikipedia.org/wiki/Inheritance_(object-oriented_programming)
  2. Herbert Schildt (2003). The complete reference C++. Tata McGraw Hill Education Private Limited. p. 417. ISBN 978-0-07-053246-5.


Superclasses

Superclasses are the parents of subclasses. [1] In relationships between two objects, a superclass is the name given to the class that passes down it's 'traits'(language entities.) [2] An example of a superclass would be an object like 'car' which would hold information like, make, model, mileage etc. An example of a Superclasses's format can be found below.

public class Person
{
}

Virtual methods

[edit | edit source]

From the wikipedia page on inheritence: "If the superclass method is a virtual method, then invocations of the superclass method will be dynamically dispatched. Some languages require that methods be specifically declared as virtual (e.g. C++), and in others, all methods are virtual (e.g. Java). An invocation of a non-virtual method will always be statically dispatched (i.e. the address of the function call is determined at compile-time). Static dispatch is faster than dynamic dispatch and allows optimisations such as inline expansion."[3]


References

[edit | edit source]


Polymorphism

In programming languages and type theory, polymorphism is the provision of a single interface to entities of different types[1] or the use of a single symbol to represent multiple different types.[2]

The most commonly recognised major classes of polymorphism are:

  • Ad hoc polymorphism: defines a common interface for an arbitrary set of individually specified types.
  • Parametric polymorphism: when one or more types are not specified by name but by abstract symbols that can represent any type.
  • Subtyping (also called subtype polymorphism or inclusion polymorphism): when a name denotes instances of many different classes related by some common superclass.[3]

History

[edit | edit source]

Interest in polymorphic type systems developed significantly in the 1960s, with practical implementations beginning to appear by the end of the decade. Ad hoc polymorphism and parametric polymorphism were originally described in Christopher Strachey's Fundamental Concepts in Programming Languages[4], where they are listed as "the two main classes" of polymorphism. Ad hoc polymorphism was a feature of Algol 68, while parametric polymorphism was the core feature of ML's type system.

In a 1985 paper, Peter Wegner and Luca Cardelli introduced the term inclusion polymorphism to model subtypes and inheritance,[2] citing Simula as the first programming language to implement it.

Types

[edit | edit source]

Ad hoc polymorphism

[edit | edit source]

Christopher Strachey chose the term ad hoc polymorphism to refer to polymorphic functions that can be applied to arguments of different types, but that behave differently depending on the type of the argument to which they are applied (also known as function overloading or operator overloading).[5] The term "ad hoc" in this context is not intended to be pejorative; it refers simply to the fact that this type of polymorphism is not a fundamental feature of the type system. In the Pascal / Delphi example below, the Add functions seem to work generically over various types when looking at the invocations, but are considered to be two entirely distinct functions by the compiler for all intents and purposes:

program Adhoc;

function Add(x, y : Integer) : Integer;
begin
    Add := x + y
end;

function Add(s, t : String) : String;
begin
    Add := Concat(s, t)
end;

begin
    Writeln(Add(1, 2));                   (* Prints "3"             *)
    Writeln(Add('Hello, ', 'Mammals!'));    (* Prints "Hello, Mammals!" *)
end.

In dynamically typed languages the situation can be more complex as the correct function that needs to be invoked might only be determinable at run time.

Implicit type conversion has also been defined as a form of polymorphism, referred to as "coercion polymorphism".[2][6]

Parametric polymorphism

[edit | edit source]

Parametric polymorphism allows a function or a data type to be written generically, so that it can handle values uniformly without depending on their type.[7] Parametric polymorphism is a way to make a language more expressive while still maintaining full static type-safety.

The concept of parametric polymorphism applies to both data types and functions. A function that can evaluate to or be applied to values of different types is known as a polymorphic function. A data type that can appear to be of a generalized type (e.g. a list with elements of arbitrary type) is designated polymorphic data type like the generalized type from which such specializations are made.

Parametric polymorphism is ubiquitous in functional programming, where it is often simply referred to as "polymorphism". The following example in Haskell shows a parameterized list data type and two parametrically polymorphic functions on them:

data List a = Nil | Cons a (List a)

length :: List a -> Integer
length Nil         = 0
length (Cons x xs) = 1 + length xs

map :: (a -> b) -> List a -> List b
map f Nil         = Nil
map f (Cons x xs) = Cons (f x) (map f xs)

Parametric polymorphism is also available in several object-oriented languages. For instance, templates in C++ and D, or under the name generics in C# and Java:

class List<T> {
    class Node<T> {
        T elem;
        Node<T> next;
    }
    Node<T> head;
    int length() { ... }
}

List<B> map(Func<A, B> f, List<A> xs) {
    ...
}

John C. Reynolds (and later Jean-Yves Girard) formally developed this notion of polymorphism as an extension to lambda calculus (called the polymorphic lambda calculus or System F). Any parametrically polymorphic function is necessarily restricted in what it can do, working on the shape of the data instead of its value, leading to the concept of parametricity.

Subtyping

[edit | edit source]

Some languages employ the idea of subtyping (also called subtype polymorphism or inclusion polymorphism) to restrict the range of types that can be used in a particular case of polymorphism. In these languages, subtyping allows a function to be written to take an object of a certain type T, but also work correctly, if passed an object that belongs to a type S that is a subtype of T (according to the Liskov substitution principle). This type relation is sometimes written S <: T. Conversely, T is said to be a supertype of S—written T :> S. Subtype polymorphism is usually resolved dynamically (see below).

In the following example we make cats and dogs subtypes of animals. The procedure letsHear() accepts an animal, but will also work correctly if a subtype is passed to it:

abstract class Animal {
    abstract String talk();
}

class Cat extends Animal {
    String talk() {
        return "Meow!";
    }
}

class Dog extends Animal {
    String talk() {
        return "Woof!";
    }
}

static void letsHear(final Animal a) {
    println(a.talk());
}

static void main(String[] args) {
    letsHear(new Cat());
    letsHear(new Dog());
}

In another example, if Number, Rational, and Integer are types such that Number :> Rational and Number :> Integer, a function written to take a Number will work equally well when passed an Integer or Rational as when passed a Number. The actual type of the object can be hidden from clients into a black box, and accessed via object identity. In fact, if the Number type is abstract, it may not even be possible to get your hands on an object whose most-derived type is Number (see abstract data type, abstract class). This particular kind of type hierarchy is known—especially in the context of the Scheme programming language—as a numerical tower, and usually contains many more types.

Object-oriented programming languages offer subtype polymorphism using subclassing (also known as inheritance). In typical implementations, each class contains what is called a virtual table—a table of functions that implement the polymorphic part of the class interface—and each object contains a pointer to the "vtable" of its class, which is then consulted whenever a polymorphic method is called. This mechanism is an example of:

  • late binding, because virtual function calls are not bound until the time of invocation;
  • single dispatch (i.e. single-argument polymorphism), because virtual function calls are bound simply by looking through the vtable provided by the first argument (the this object), so the runtime types of the other arguments are completely irrelevant.

The same goes for most other popular object systems. Some, however, such as Common Lisp Object System, provide multiple dispatch, under which method calls are polymorphic in all arguments.

The interaction between parametric polymorphism and subtyping leads to the concepts of variance and bounded quantification.

Row polymorphism

[edit | edit source]

Row polymorphism[8] is a similar, but distinct concept from subtyping. It deals with structural types. It allows the usage of all values whose types have certain properties, without losing the remaining type information.

Polytypism

[edit | edit source]

A related concept is polytypism (or data type genericity). A polytypic function is more general than polymorphic, and in such a function, "though one can provide fixed ad hoc cases for specific data types, an ad hoc combinator is absent".[9]

Implementation aspects

[edit | edit source]

Static and dynamic polymorphism

[edit | edit source]

Polymorphism can be distinguished by when the implementation is selected: statically (at compile time) or dynamically (at run time, typically via a virtual function). This is known respectively as static dispatch and dynamic dispatch, and the corresponding forms of polymorphism are accordingly called static polymorphism and dynamic polymorphism.

Static polymorphism executes faster, because there is no dynamic dispatch overhead, but requires additional compiler support. Further, static polymorphism allows greater static analysis by compilers (notably for optimization), source code analysis tools, and human readers (programmers). Dynamic polymorphism is more flexible but slower—for example, dynamic polymorphism allows duck typing, and a dynamically linked library may operate on objects without knowing their full type.

Static polymorphism typically occurs in ad hoc polymorphism and parametric polymorphism, whereas dynamic polymorphism is usual for subtype polymorphism. However, it is possible to achieve static polymorphism with subtyping through more sophisticated use of template metaprogramming, namely the curiously recurring template pattern.

See also

[edit | edit source]


Abstraction

If you've ever made coffee using a coffee machine, you have probably experience abstraction. When you make your coffee, you make sure that there are ample coffee beans, you add water, add milk, and perhaps put in a fresh coffee filter. Then, you hit a button and a coffee of your choice is made. This is a good example of abstraction since the user doesn't need to be concerned about the right amount of milk needed for a specific type of coffee, or how many beans need to be ground up for the right amount, or what the temperature of the water needs to be, the user is only considered with providing the necessary materials to produce the final product. This same structure applies to object oriented programming. The user of the object should only be concerned with providing the necessary parameters for the final product and shouldn't really know or need to know how the object works internally.

Abstract classes and interfaces

[edit | edit source]

In some languages it is possible to create a class which can’t be instantiated. That means that we can’t use this class directly to create an object – we can only inherit from the class, and use the subclasses to create objects.

Why would we want to do this? Sometimes we want to specify a set of properties that an object needs to have in order to be suitable for some task – for example, we may have written a function which expects one of its parameters to be an object with certain methods that our function will need to use. We can create a class which serves as a template for suitable objects by defining a list of methods that these objects must implement. This class is not intended to be instantiated because all our method definitions are empty – all the insides of the methods must be implemented in a subclass.

The abstract class is thus an interface definition – some languages also have a type of structure called an interface, which is very similar. We say that a class implements an interface if it inherits from the class which specifies that interface.

In Python we can’t prevent anyone from instantiating a class, but we can create something similar to an abstract class by using NotImplementedError inside our method definitions. For example, here are some “abstract” classes which can be used as templates for shapes:

class Shape2D:
    def area(self):
        raise NotImplementedError()

class Shape3D:
    def volume(self):
        raise NotImplementedError()

Any two-dimensional shape has an area, and any three-dimensional shape has a volume. The formulae for working out area and volume differ depending on what shape we have, and objects for different shapes may have completely different attributes.

If an object inherits from 2DShape, it will gain that class’s default area method – but the default method raises an error which makes it clear to the user that a custom method must be defined in the child object:

class Square(Shape2D):
    def __init__(self, width):
        self.width = width

    def area(self):
        return self.width ** 2

References

[edit | edit source]

See also

[edit | edit source]


Advanced Concepts

Advanced Concepts

See also

[edit | edit source]


State?

State Machines

[edit | edit source]

If you are unfamiliar with state machines, we won't delve too deep into them, but we highly recommend you look into them more. It's good for your clarity of thought process. Pedantically speaking, state machines are somewhat complicated mathematical constructs, and there are many interesting results that can be derived using the mathematical notation. But that's not our interest. Our interest is two-fold:

  • For one, state machines provide a clear graphical way to illustrate potentially complicated application logic. First, you identify what your possible states are. For example, a server might be IDLE, CONNECTED, BUSY, ERROR, etc. Then, you define all the possible inputs, i.e. messages that the client might send, and for each state diagram the server's response to each input. This helps promote thoroughness as you think about reactions to unanticipated messages in various states.
  • Secondly, state machines are a standard tool with a relatively standard notation. There are tools and libraries that will automatically make code given some canonical state machine format as input. This is effectively what regular expression processors do.

In object oriented programming, "objects" are sometimes implementations of state machines. State machines are a better conceptual underpinning for behavioral entities —servers, regular expression processors, graphics engine, and virtual machines are all good examples of behavioral entities.

A basic distinction between simple objects and behavioral entities is that simple objects do not change their behavior based on their value. In fact, we tend to refer to their value rather than state. Simple objects tend to be kept in collections and searched through, passed to and returned from functions, and in general act like values. String and Rectangle are classic examples. Later on, we'll discuss message passing as an abstraction of method invocation, and it may seem a little strange in the context of simple objects.

If you are still having trouble with the concept of objects, download and play with BlueJ, Most people "get it" within hours of using that IDE.

"State" is Evil!

[edit | edit source]

Let's look at an example, now in Ruby:

We're displaying something over time, so we're dealing with both pixels and seconds. Based on the zoom level, there is a correlation between them: pixels per second, or PPS.

class Timeline
 {
  PPS;
  current_position_in_seconds;
  current_position_in_pixels;
 public:

  GetPosInSeconds() {current_position_in_seconds;}
  SetPosInSeconds(s) {current_position_in_seconds = s;} # oops, we're out of sync
  GetPosInPixels() {current_position_in_pixels;}
  SetPosInPixels(p) {current_position_in_pixels = p;} # oops, we're out of sync
}

In this example, we're maintaining gratuitous state — which is to say, we're holding the position in both seconds AND pixels. This is convenient, and important for users of this class, but we've messed up the encapsulation here. Whenever you set the value in one unit, you've destroyed the correctness for the other unit. Okay, you say, here's a simple fix:

class Timeline
{
  PPS;
  current_position_in_seconds;
  current_position_in_pixels;
public:

  GetPosInSeconds() {current_position_in_seconds;}
  SetPosInSeconds(s)
       {
          current_position_in_seconds = s;
          current_position_in_pixels = s*PPS;
        }

  GetPosInPixels() {current_position_in_pixels;}
  SetPosInPixels(p)
       {
          current_position_in_pixels = p;
          current_position_in_seconds = p/PPS;
       }
}

This fixes the obvious error with the previous example, but you've created a lot of work for yourself. Now you have to keep every calculation in every method in the Timeline class duplicated to maintain consistency. There could be dozens more where this came from (trust me). And what about when you add in other units, say inches? You have to duplicate all of that again. Presumably, you see where this is heading: calculated properties, logical properties, virtual properties, whatever you like to call them. Let's see this example again:

class Timeline
{
  PPS; # Pixels Per Second
  IPS; # Inches Per Second
  current_position_in_seconds;
public:

  GetPosInSeconds() {current_position_in_seconds;}
  SetPosInSeconds(s) {current_position_in_seconds = s;}
 
  GetPosInPixels() {current_position_in_seconds*PPS;}
  SetPosInPixels(p) {current_position_in_seconds = p/PPS; }
 
  GetPosInInches() {current_position_in_seconds*IPS;}
  SetPosInInches(i) {current_position_in_seconds = i/IPS; }
}

Now, we're presuming that the conversion factors of PPS and IPS are set correctly for simplicity, but otherwise we've vastly simplified our problem. In twenty other functions we now write, we only have to worry about seconds, there's no consistency problems to worry about. Additionally, if we need to change our base units from seconds to pixels, the Timeline users need never know.


Interfaces

Interfaces

[edit | edit source]

An Interface allows a class to expose a different set of properties and methods depending on context. For example, say that you were writing a Visual Basic 2005 application that manages clients. A client could be an individual person, or an organization. Part of the program generates address labels for clients.

You might define a person like this:

Class Person
    Public LastName As String
    Public FirstName As String
    Public Address As String
End Class

You might also define an Organization like this:

Class Organization
    Public Name As String
    Public MailingAddress As String
    Public BuildingAddress As String
End Class

As you can see, the main differences between a Person and an Organization are:

  • a Person has both a first name and a last name, while an organization has only a single name
  • an Organization has a separate mailing address, while a Person only has one address

So that you can have the one routine to print address labels, you create an interface called "IAddressLabel" (by convention, the name of an interfaces should start with a capital I).

Interface IAddressLabel
    ReadOnly Property Name() As String
    ReadOnly Property Address() As String
End Interface

You now change the Person class to implement this interface. The main thing to notice is that the Person's first and last names are combined on the label.

Class Person
    Implements IAddressLabel
    Public LastName As String
    Public FirstName As String
    Public Address As String
    Public ReadOnly Property Address1() As String Implements IAddressLabel.Address
        Get
            Return Address
        End Get
    End Property
    Public ReadOnly Property Name() As String Implements IAddressLabel.Name
        Get
            Return FirstName & " " & LastName 'combine the first and last names for the address label
        End Get
    End Property
End Class

You now change the Organization class to implement the IAddressLabel interface. Notice that the address for the label is the mailing address, not the building address.

Class Organization
    Implements IAddressLabel
    Public Name As String
    Public MailingAddress As String
    Public BuildingAddress As String
    Public ReadOnly Property Address() As String Implements IAddressLabel.Address
        Get
            Return MailingAddress 'Use the mailing address for the address label
        End Get
    End Property
    Public ReadOnly Property Name1() As String Implements IAddressLabel.Name
        Get
            Return Name
        End Get
    End Property
End Class

Now you can group the disparate objects together as a collection of address labels!

Sub Main()
    'create a person object
    Dim Bill As New Person
    Bill.FirstName = "William"
    Bill.LastName = "Jones"
    Bill.Address = "123 Test Rd Testville 9123"
    'create another person object
    Dim Sally As New Person
    Sally.FirstName = "Sally"
    Sally.LastName = "Smith"
    Sally.Address = "999 Sample Ave Testburg 9222"
    'create an Organization object
    Dim WidgetsInc As New Organization
    WidgetsInc.Name = "Widgets Incorporated"
    WidgetsInc.BuildingAddress = "9123 Avenue Rd Sampletown 9876"
    WidgetsInc.MailingAddress = "P.O. Box 123 Sampletown 9876"
    'Because all three objects implement the IAddressLabel interface, we can put them in the same array
    Dim MailingList(2) As IAddressLabel
    MailingList(0) = Bill
    MailingList(1) = Sally
    MailingList(2) = WidgetsInc
    'loop through, displaying the address label for each object
    Dim i As Integer
    For i = 0 To 2
        MsgBox(MailingList(i).Name & vbCrLf & MailingList(i).Address)
    Next i
End Sub

Interfaces vs. Inheritance

[edit | edit source]

Interfaces and Inheritance can both be used to solve the problem of treating dissimilar objects collectively. For example, if you have a Cat class and a Dog class, but have some routine that needs to process them together, you could either:

  • Create an Animal base class, which contains procedures common to both Cats and Dogs, and have Cat and Dog inherit from Animal, or
  • Create an IAnimal interface, and have both Cat and Dog implement the interface.

Which approach to use depends on several factors. In general:

  • If you are extending an existing class, then use inheritance. For example, if you already have an Animal class, and then discover a need to distinguish between Cats and Dogs
  • If you are simply wanting to treat different objects as the same, then use Interfaces. For example, you already have Cat and Dog classes, and then discover a need to manipulate them in a similar fashion

The introduction of Components

[edit | edit source]

Delegation

[edit | edit source]

Inheritance vs Delegation

[edit | edit source]

Dynamic Inheritance

[edit | edit source]


See also

See Also:

[edit | edit source]

Sources used in this book:

[edit | edit source]

Wikipedia: Object oriented programming
https://www.thoughtco.com/what-is-inheritance-2034264

Wikipedia.org/wiki/Class_(computer_programming)

Python Programming/Classes

Wikipedia:Property(programming)

Wikipedia:Method(Computer programming)

Wikipedia:Constructors

Open Books project:Python3-Classes and Objects

Wikipedia:Mutator method

Programming Fundamentals/encapsulation

Wikipedia:Encapsulation_(computer_programming)

Wikipedia:Polymorphism_(computer_science)

Programming Fundamentals/Inheritance and Polymorphism

Wikipedia:Abstraction(computer science)

Wikipedia:Programming_paradigm

TI-Basic Z80 Programming/Advanced Programming

Additional resources(Language Specific)

[edit | edit source]

JavaScript:

[edit | edit source]

Object-oriented Programming in 7 minutes | Mosh

Python:

[edit | edit source]

Wikibooks: Introduction to Python Programming

Wikibooks: Python Programming

Unit-Testing

Additional resources(Non-Language specific)

[edit | edit source]

Wikibooks: Introduction to Programming

Wikibooks: Programming Fundamentals