Thinking about Objects

Last time we looked at cleaning up the global space. We cleaned up the clutter in the global namespace by encapsulating lots of little bits of data into user defined types.

But even after doing that, there are times when there are lots of different routines that need access to that data. You might be tempted to go ahead and make the user-defined type global so you don’t have to keep passing it around to everything. But there is a better way.

ofromcSuppose you’re building an ordering system. There is a ton of data for an order — the customer, the billing address, the shipping address, the items on the order, the shipping details, and whatever else you might need. That’s probably several user defined types all bundled up into one, but for the moment don’t worry about all that. Just think about the order as a whole — one big piece of data.

But an order isn’t just data. It’s a thing. It’s somebody’s request for something to happen. That order may trigger actions, like something being manufactured, or something being shipped, or money changing hands. It might have to be printed, or recorded in a database, or emails sent, or whatever. All of those things are actions that get taken on an order.

So your order has a bunch of data associated with it, and it has a bunch of actions associated with it. That’s an object.

At its simplest level, an object is just a thing which has data (attributes), and actions it can perform (methods). That’s it. But used properly, they can transform your software development.

Consider this oversimplified example:

VB.Net

Public Class Order
    Public CustomerInfo as Customer
    Public BillingAddress as Address
    Public ShippingAddress as Address
    Public Items as List(Of OrderItem)
    
    Public Sub New(buyer as Customer, billing as Address, shipping as Address, items as List(Of OrderItem))
        Me.Customer = buyer
        Me.BillingAddress = billing
        Me.ShippingAddress = shipping
        Me.Items = items
    End Sub
    
    Public Function Process()
        Me.Save()
        Me.Manufacture()
        If Me.HasErrors() Then
            NotifyProductionManager(Me)
        Else
            Me.PrintPaperwork()     
            Me.Ship()
            Me.NotifyCustomer()
            Me.MarkComplete()
        End If 
    End Function

    ' other methods would follow
End Class

C#

public class Order
{
    public Customer CustomerInfo;
    public Address BillingAddress;
    public Address ShippingAddress;
    public List(OrderItem) Items;
    
    public Order(Customer buyer, Address billing, Address shipping, List(OrderItem) items)
    {
        this.Customer = buyer;
        this.BillingAddress = billing;
        this.ShippingAddress = shipping;
        this.Items = items;
    }
    
    public Process()
    {
        this.Save();
        this.Manufacture();
        if(this.HasErrors())
            NotifyProductionManager(this)
        else
        {
            this.PrintPaperwork();
            this.Ship();
            this.NotifyCustomer();
            this.MarkComplete();
        }
    }

    // other methods would follow
}

PHP

public class Order
{
    var $Customer; 
    var $BillingAddress;
    var $ShippingAddress;
    var $Items;
    
    public Order(Customer $buyer, Address $billing, Address $shipping, array $items)
    {
        $this->Customer = buyer;
        $this->BillingAddress = billing;
        $this->ShippingAddress = shipping;
        $this->Items = $items;
    }
    
    public Process()
    {
        $this->Save();
        $this->Manufacture();
        if($this->HasErrors())
            NotifyProductionManager(this);
        else
        {
            $this->PrintPaperwork();
            $this->Ship();
            $this->NotifyCustomer();
            $this->MarkComplete();
        }
    }

    // other methods would follow
}

Python

class Order:
    def __init__(self, buyer, billing, shipping, items):
        self.Customer = buyer
        self.BillingAddress = billing
        self.ShippingAddress = shipping
        self.Items = items

    def Process(self):
        self.Save()
        self.Manufacture()
        if(self.HasErrors())
            NotifyProductionManager(self)
        else:
            self.PrintPaperwork()
            self.Ship()
            self.NotifyCustomer()
            self.MarkComplete()

    # other methods would follow

Obviously there is a lot that isn’t shown here so don’t get hung up on the details. They don’t matter. What matters is that this order object can now behave like an order. It is a self-contained thing that knows what data it needs to know, and can perform what actions it needs to perform. All of the data about an order is available to any of the methods of the order object, because they are part of the object. No need for globals.

But isn’t it the same thing, you say? If the variables can be accessed by anything in the class, then how are they different from globals?

Good question. And the answer is no. It’s not the same thing. For one thing, the variables in the object belong to just that object. You could have two different order objects at the same time and they could each have completely different order information. You can’t do that with globals. Even if you only ever have one order object at a time, you still benefit from the data being part of the object. There’s no danger of a leftover item from a previous order accidentally getting tacked onto this one because some global didn’t get cleared out between orders. When you create a new order object, it gets all new data. Any data in any other object doesn’t even exist, as far as your new object is concerned. It only knows about its own data, not anybody else’s. And it can’t step on anyone else’s data either — you can be printing one order while you’re saving another and processing a third. All the data will be right where it belongs.

You can also encapsulate and manage the member variables of an object in various ways that are not as easy with globals. For example, you could make the Customer member protected. Then nothing but the object or its descendants could change it. You could make it private — then nothing but the class itself could change it, not even descendants. You could wrap it in a property — then you could completely control all access to it, no matter where it came from. You could make it readonly, so it can only be set when the class is created, but can’t be changed after that. You won’t have to guess who changed that variable, or why, or in what module. You can ensure that all access to that variable comes down a specific pathway. Because of that, debugging, testing, and maintenance will be so much easier.

This entry was posted in Fundamentals, Programming. Bookmark the permalink.

One Response to "Thinking about Objects"

Leave a Reply

Your email address will not be published. Required fields are marked *


*