Porting procedural code, such as C, to an object-oriented framework can be overwhelming to architects new to object-oriented design. However, once you’ve completed the move, you can take advantage of one of the best features of object-oriented programming—code reuse.
Typically, you can transfer a procedure library into the object-oriented language and design by moving the methods into a static noninstantiated object. This class can reside in a utilities package.
When you port over the library code in this manner, you’ve accomplished a simple transfer, but the code is not considered object-oriented since there isn’t any encapsulation of the logic within the objects that use code in the utilities package. In fact, the presence of a utilities package in your object-oriented design is a sure sign that your work is not complete—that is, you have procedural-based design elements in your application—because references to the utilities package expose exactly how an object converts specific data, which violates encapsulation.
Your next step, then, is to take this procedural code and make it fit the requirements of object-oriented design. You accomplish this through a process called refactoring.
Refactoring is the process of modifying the internal structure of code without altering expected behavior. You can think of it as being similar to design patterns, in that design patterns and refactoring both attempt to establish a foundation for developers to manipulate code.
In this case, you’ll use a two-part refactoring process. First, you’ll move the static methods of the ported code into the objects that utilize those methods. Then, you’ll move common functions into objects.
Martin Fowler’s book Refactoring:
Improving the Design of Existing Code provides an excellent explanation
and catalog of refactorings. Your first refactoring step, in which you convert
the procedural methods, will be similar to the refactoring called the Move method, which
is outlined in Fowler’s book. This entails moving the relevant method into the
class that uses it the most.
Next, you’ll use the Extract Class method to move common functions into objects. The Extract Class method entails creating a class to replace frequently used methods and data. Doing so creates an object that encapsulates the method’s behavior. The most likely candidate for this change would be an object that frequently calls the ported procedural method.
The small utility class in Listing A has a fewroutines that can be used from any other class that includes the util package, but there is not any encapsulation of logic within the objects that use code in the utilities package. This version of the ByteUtil class consists of a large number of static methods that manipulate byte arrays and hexadecimal strings. The key to this type of class is that it must not be instantiated, which is enforced by the private constructor. All the methods are declared static so they can be accessed by the class name, such as:
Next, we implement the Extract Class method. Using the unsignedByteToInt method
as an example, we can create a class that operates with byte values and supplies
a method called intValue. This class could also support a stringValue method to
be utilized in making hexadecimal strings from byte values, as shown in Listing B. Notice theencapsulation of the data and hiding
of the conversion details from the objects that use the new class.
At this point, the entire procedural library would be represented in various objects that contain the methods once present in the static library. To test for a successful port, remove the static library and verify that the build is still valid.