Using OO in business applications
An article by Chip Camden, based on his presentation at Synergex's Success Partner's Conference 2007
What is OO?
Object orientation is both a design philosophy and a set of programming language features that support it. Languages that provide such features are often called object-oriented programming (OOP) languages. The primary goal of object orientation is to make code describe the real-world objects involved in the application, as well as their interactions and relationships, thereby reducing the need for translation between business rules and computer instructions.
How does object-oriented programming apply to business applications?
Business processes can be modeled in code. For example, if you are working with an order entry system, you could make “Order” a class that contains all the data related to an order, as well as the methods needed for an order. Since a class defines a type of object, an Order class will contain some general information about an order and a collection of line items, along with methods to retrieve, commit, and maybe validate that data.
You might then find that sales orders and purchase orders have a lot in common, but still differ somewhat. You could then make both a SalesOrder class and a PurchaseOrder class that “extend” the Order class—in other words, that add data and procedures that are unique to each and possibly override some behaviors and properties of the Order base class.
As you apply this principle to more areas of your application, you might start to see patterns emerge. A lot of what you do to store, retrieve, update, delete, and list orders also applies to other types of tables. You could then make the Order class extend an even more generic class that provides methods and data for manipulating any type of discrete data item. If you’re familiar with Design Patterns, you might want to follow the Model-View-Controller pattern and call this class a Model. Then you could create a View class which represents any type of user interface for the data, and extend that for specific ways of looking at certain types of data (input windows, lists, etc.). Finally, a Controller class could be used to orchestrate the overall behavior of an application, and it could be extended to create specific types of applications.
However, that strategy could constitute a complete rewrite, which is not the best option for any application that has taken many years to mature. Instead, you need to incrementally incorporate newer methods and technologies in a way that allows you to continue to release your product on a regular basis.
The best way to begin using objects
Most people begin to use objects without even really thinking about OOP. Many begin by using some of the supplied classes, such as the new dynamic Array class that allows you to size an array at runtime without having to use the obtuse ^m syntax. In this situation you could be using classes without even realizing it.
Or it’s possible that you’ll begin using the new try-catch-finally structured exception handling in Synergy 9. As soon as you catch your first exception, you’ll have an Exception object. You’ll see how naturally you can access the information for an exception by using the new object syntax. Maybe you’ll create your own derived class of exception in order to add your own custom information about an error encountered in your application. That might be your very first class definition.
Then you’ll start to discover more of the useful features of objects. For instance, scope and destructors. How many times have you wrestled with problems related to cleaning up resources when they’re no longer needed? UI Toolkit provides the environment concept to release all resources that were allocated in an environment when that environment level is exited, which works fine for many cases. But in more complex applications, you may often find that you need to preserve some resources across environment-level bounds, so you promote those to global. But then you have to remember to release them when you’re done. Wouldn’t it be nice if the resource would just get cleaned up automatically as soon as you lose all references to it? Classes enable you to do that.
As you begin to think more in terms of objects, it will become more natural for you to create classes that model other aspects of your application. But don’t rush yourself. And don’t massively redesign, unless you’ve got a lot of extra time on your hands.
A class should reflect a single type of object. That means it’s a noun, some actor within your application. Without referring to existing code, try to describe in natural language how your application works from the user’s perspective. Whenever you encounter a noun, it’s a candidate for a class.
Methods are verbs that describe some action that a class of object can perform on other objects or on itself (or intransitively). If the name of the method doesn’t denote a discrete action, then maybe it needs to be thought out differently.
Properties can be seen as adjectives that describe the object, or members that compose it. If a class has a property that you can’t think of in one of these ways, perhaps it shouldn’t be a property of this class.
A class should extend another class if it passes the “is a” test. A Bugatti is a car, so Bugatti extends the class of cars. A Bugatti is not a chassis. A Bugatti contains a chassis; it isn’t derived from it. That distinction trips up class designers all the time. For instance, given the Model class we discussed earlier (from which an Order is derived), does it extend or contain a database table?
Often you can get away with inaccurate inheritance models for a while. But eventually the practice of deriving classes from things that they logically contain causes conflicts, because you end up wanting to derive them from more than one ancestor class. Use the “is a” test to avoid that problem.
Signs that a class is derived from something that it should have contained:
–You feel the need for multiple inheritance
–You add way too many methods
–You don’t override any virtual methods
Signs that a class contains something that it should have been derived from:
–You replicate all of the same methods and just forward them to the same method of a contained component
What else to avoid
OO rewards a good design handsomely, but punishes a bad design to the end of your days. Here are some common pitfalls.
In Execution in the Kingdom of Nouns, Steve Yegge describes how Java’s (and C#’s) insistence on requiring all functions to belong to a class causes programmers to manufacture many needless nouns in order to perform any activity. Don’t give it an actor if it doesn’t need one. You don’t have to fall into that trap in Synergy/DE because Synergy/DE provides stand-alone functions. If your description of a process begins with a verb, just make it a function, not a class.
Classes whose names include the words “manager,” “broker,” “locator,” or any other verbal noun, should be suspected. If the noun’s purpose in life is merely to perform some action, then it should be a function instead of a class.
What about singleton classes? A singleton class is instantiated only once. Sometimes that means that a singleton is really just a set of global data and functions disguised as a class. Take, for instance, an Application object. Java and C# virtually require classes like this because otherwise you can’t get anything done. Because these languages require that you have a class to contain any function, you typically pick yourself up by your bootstraps by creating an Application object that has, at least, some sort of “run” method. You don’t need that in Synergy/DE, unless it’s useful. Singleton classes can be useful for encapsulating related data and functions together, creating derivations of similar applications, and for avoiding global naming conflicts—but be careful not to let them become big, miscellaneous messes or just the equivalent of namespaces.
In OOP and the death of modularity, Chad Perrin notices a trend in object-oriented programming in which classes become coupled with one another, reducing modularity. Sometimes, by thinking in terms of objects rather than starting with processes, you can bundle too much functionality together into one class. Interfacing with such a complex class soon requires complex knowledge about how that class operates, creating unnecessary dependencies. By contrast, keeping the design simple and atomic requires less intimate knowledge and maximizes reusability.
Geek24 provides a series of humorous programming quips, including: “The great thing about Object Oriented code is that it can make small, simple problems look like large, complex ones.” Unfortunately, that often proves true. But you can avoid doing that, and make OOP work for you instead.
Avoid creating overly ornate inheritance hierarchies. Focus on the abstractions that you use in the business model, and ignore the rest. In real life, we always ignore certain layers of abstraction. It’s useful, for instance, to talk about an overnight envelope as a type of shipment, but we rarely need to state the fact that all shipments are types of molecular collections. That’s not the level on which we operate. Likewise within an object hierarchy, don’t bother including ancestor classes that no one will ever need. Make use of inheritance where it makes sense.
A good rule of thumb: simplify. If adding a class simplifies the code and makes it easier to understand and manipulate, that’s a good design. If it complicates the code and adds unnecessary layers and dependencies, you were better not to use objects at all. The beauty of Synergy/DE is that it lets you decide.