You mention two books in which one of the primary messages is "The Boy Scout Rule" i.e. clean up the code as you touch it. If you have a working system, a massive rewrite is counter-productive. Instead, as you add new functionality, make sure you improve the code as it stands.
- Write unit tests to cover the existing code that you need to change.
- Refactor that code so it is more pliable for change (making sure your tests still pass).
- Write tests for the new/revised functionality
- Write code to make the new tests pass
- Refactor as necessary.
To dive further in, Feathers talks about testing the application at its seams: the logical points at which the units connect. You can take advantage of a seam to create a stub or a mock for a dependency so that you can write tests around the dependent object. Let's take your AddressBO as an example
public class AddressBO
{
public TransferObject GetAddress(string addressID)
{
if (StringUtils.IsNull(addressID))
{
throw new ValidationException("Address ID must be entered");
}
AddressDAO addressDAO = new AddressDAO();
return addressDAO.GetAddress(addressID);
}
}
There is an obvious seam between the AddressBO and the AddressDAO. Let's create an interface for the AddressDAO and allow the dependency to be injected into the AddressBO.
public interface IAddressDAO
{
TransferObject GetAddress(addressID);
//add other interface methods here.
}
public class AddressDAO:GenericDAO, IAddressDAO
{
public TransferObject GetAddress(string addressID)
{
///implementation goes here
}
}
Now you doctor up your AddressBO to allow for injection
public class AddressBO
{
private IAddressDAO _addressDAO;
public AddressBO()
{
_addressDAO = new AddressDAO();
}
public AddressBO(IAddressDAO addressDAO)
{
_addressDAO = addressDAO;
}
public TransferObject GetAddress(string addressID)
{
if (StringUtils.IsNull(addressID))
{
throw new ValidationException("Address ID must be entered");
}
//call the injected AddressDAO
return _addressDAO.GetAddress(addressID);
}
}
Here we're using "poor man's dependency injection." Our only goal is to break the seam and allow us to test the AddressBO. Now in our unit tests we can make a mock IAddressDAO and validate the interactions between the two objects.
It seems that C has its own quasi-objects such as 'structs' that can be considered as objects
Let's together you and I read through the Wikipedia page on object oriented programming and check off the features of C-style structs that correspond to what is traditionally considered to be object-oriented style:
(OOP) is a programming paradigm using "objects" – data structures consisting of data fields and methods together with their interactions
Do C structs consist of fields and methods together with their interactions? No.
Programming techniques may include features such as data abstraction, encapsulation, messaging, modularity, polymorphism, and inheritance.
Do C structs do any of these things in a "first class" way? No. The language works against you every step of the way.
the object-oriented approach encourages the programmer to place data where it is not directly accessible by the rest of the program
Do C structs do this? No.
An object-oriented program will usually contain different types of objects, each type corresponding to a particular kind of complex data to be managed or perhaps to a real-world object or concept
Do C structs do this? Yes.
Objects can be thought of as wrapping their data within a set of functions designed to ensure that the data are used appropriately
No.
each object is capable of receiving messages, processing data, and sending messages to other objects
Can a struct itself send and receive messages? No. Can it process data? No.
OOP data structures tend to "carry their own operators around with them"
Does this happen in C? No.
Dynamic dispatch ... Encapsulation ... Subtype polymorphism ... Object inheritance ...
Open recursion ... Classes of objects ... Instances of classes ... Methods which act on the attached objects ... Message passing ... Abstraction
Are any of these features of C structs? No.
Precisely which characteristics of structs do you think are "object oriented"? Because I can't find any other than the fact that structs define types.
Now, of course you can make structs that have fields that are pointers to functions. You can make structs have fields that are pointers to arrays of function pointers, corresponding to virtual method tables. And so on. You can of course emulate C++ in C. But that is a very non-idiomatic way to program in C; you'd be better off just using C++.
And also, C files themselves are basically separate "modules", right? Then aren't modules kind of like 'objects' too?
Again, what characteristics of modules are you thinking of that makes them act like objects? Do modules support abstraction, encapsulation, messaging, modularity, polymorphism, and inheritance?
Abstraction and encapsulation are pretty weak. Obviously modules are modular; that's why they're called modules. Messaging? Only in the sense that a method call is a message and modules can contain methods. Polymorphism? Nope. Inheritance? Nope. Modules are pretty weak candidates for "objects".
Best Answer
There is nothing so bad that it cannot be used as a bad example, at least. To me, your example is not even "formally object oriented", it is just as procedural as the original. The only noteable thing you changed is the namespace where the search function "lives".
A customer object should represent the data and some business logic of a customer (there is not even one attribut in your class, and the search method does not work on a customer object). A "search" function for customers could return a customer object, or a collection of customer objects (echoing some json string has nothing to do with that). Such a function is probably not a member function of a customer class (it could be placed there as a static function, or somewhere else, for example, in a
CustomerFactory
). And a reasonable customer search should avoid coupling to global attributes and methods (like $options, Db, or Auth::authenticate).So if your goal is to introduce more "object oriented structure" into your program, you may be better off by doing a little bit more upfront design first. For example, start by designing a customer class first and think about how a search function must look like for delivering such objects.