Object Oriented Programming (OOP) builds upon years of traditional software engineering principles. The core tenets of software engineering are known by most programmers in principle if not always in practice.
- Low or loose coupling
Each of the principles will be briefly dicussed below.
Traditional Software Engineering
Abstraction is the goal of building things so that users of objects can use them without worrying or even caring about the detail of the construction or internal implementation of the objects. Complex objects should be abstracted into less complex building blocks which in turn can be used to build other objects. Therefore good abstraction should consist of objects built from other objects which themselves are only slightly less complex.
For the purpose of this article we'll look at an LCD television as an example. Internally it is a very complex electronic device, but if one looks at it as a software engineering object the principles we are discussing here can be easily grasped.
The LCD TV itself consists of a number of very complex objects which have been abstracted into a single purpose device (for watching television signals) whose internal implementation most users have no desire to truly understand. Ordinary users can switch on the TV, change channels, adjust sound and picture without even thinking about the power system, the tuner, the amplifiers and transistors and microchips that do all of this work.
Loose or low coupling is the principle of restricting the quantity and complexity of links between objects. For objects to be reusable, they must be able to work easily with other objects without necessarily having been designed with those particular objects in mind.
We define objects 'coupling' in terms of interface and implementation. Loosely coupled objects have well-defined interfaces. The interface defines WHAT the inputs and outputs of an object will be. It does not indicate HOW the inputs will be processed or HOW the outputs will be arrived at. That aspect - the implementation - is hidden from the users.
In our LCD TV example, we can be sure that our TV will work with electricity (provided it is of the correct voltage) regardless of where or how that electricity was generated because we are confident of the electrical interface definition, even if we don't really understand the electronic circuits that control the power to the TV. We can also be confident that it will work with standard television signal formats. The way it does this - the implementation - will vary from manufacturer to manufacturer and even from model to model from the same manufacturer. But when we plug in an antenna or Set Top Box we use the interfaces provided (SCART/RGB etc) and we expect the TV to work.
A well designed object must do one thing and one thing only (and hopefully do it well). Abstraction of object should be done with cohesion in mind: that is objects should be abstracted to the point that a self-contained object with a well-defined interface can do a task or fulfill an objective.
The LCD TV consists of many parts. But each part is cohesive: the tuner does tuning, the speaker produces sound, the LCD display provides the picture and so on. Every part of the TV does one thing. If any part does more than one thing, in theory at least it should be abstracted into two objects. (I say in theory because in a pragmatic sense this may not be desirable: mass produced components are often designed for use in more than one application, so could concievably do more than one thing.)
Objects with well-defined interfaces allow their usage to be modular. Modularisation simplifies reuse of objects because objects with the same interface can be interchanged easily even if the implementation in the newer object is very different.
It doesn't matter what kind of TV you have, if you plug an antenna into a TV you can expect a picture because the TV signal interface is so well defined. The whole TV is modular, and if your old TV breaks you don't have to change your VCR, DVD player or satellite reciever: a new TV will respond to all these inputs and outputs as well, even if it is a completely different technology such as a plasma or laser TV.
Object oriented principles build upon these adding a new layer which interrelate to the traditional software engineering core principles but introduce new concepts which properly implemented can vastly improve the re-usability, robustness and longevity of the code. The OO principles are
The image below shows this hierarchy of principles:
Encapsulation is the practice of hiding or abstracting the implementation of objects. Objects are used through an interface to the 'capsule'.
To continue the theme of our LCD TV, the complex technology of the television is hidden away inside a shiny box which is operated upon through a set of buttons or menus which even non-technical people can use. These operating interfaces are usually common to televisions even if the televisions themselves implement the controls (oblivious to the users) in very different ways.
Inheritance is the principle of defining objects in terms of other simpler objects. Those objects in turn may be defined in terms of even more simple objects. Inheritance allows us to define objects in the simplest terms within a 'super-class'. We can then inherit these characteristics into 'sub-classes' which we can make more complex. The sub-class will have all the characteristics of the super-class including interfaces but it can introduce additional interfaces or properties. It may even implement operations differently to the superclass. Note however, that the traditional goal of cohesion must be maintained in all inherited classes: that is sub-classes will also do the one thing the parent class was designed for.
The principle of inheritance allows us to define the core characteristics of objects just once in a super-class, then focus on the differences and intricacies in the sub-classes. This minimises code repetition.
At a fundamental level every television would be expected to display audio-visual information based upon the television signal recieved. We could therefore imagine an abstract super-class of 'Television' which defines a 'Display' interface. We don't necessarily care at this level how the images and sound will be produced from the signal because the super-class here is abstract (not a real world object). We derive new types which inherit the Television type: LCD, Plasma and CRT. The CRT displays pictures with a cathode-ray tube. 'CRT' would be a real-world type and we would implement the 'Display' operation appropriately for this technology. This would be repeated for LCD and Plasma and in each case 'Display' would need a different implementation. Users of any of these televisions however will not need to understand this. When they press the 'ON' button on any 'Television', the display will be activated.
This carries the principle of modularisation to another level. Polymorphism is the idea that the same result can be obtained in different ways without user knowledge or additional effort.
A user operating a television is not concerned about the origin of the television signal or the medium through which it has arrived at his set. It could have come a local television station or on the other side of the world and it could have come via free-to-air, cable, broadband, satellite etc. Nevertheless, the television will display any of these signals and the user does not generally need to operate the television differently when watching any of these signals.
Polymorphism and inheritance work hand in hand in that one kind of television may process these signals completely different ways from another, yet because all televisions implement the same interfaces they can be operated in the same way.
No doubt there is a great deal more to OO than simply being able to quote these principles. Nevertheless, much of the practical application of OOP relies on at least a basic appreciation of these fundamentals. Without these many of the commonly used patterns and practices would not make any sense, and even if one does not actively work to any formal software patterns in ones work, knowing these principles can be a helpful guide when one designs the structure of any application.