How do professional programmers make judgement call on whether to go
for OOP or not? It would be really helpful for me.
For me, there are two decision points. First, sometimes it will be obvious at the beginning. There will be lots of similar types that all share common methods that differ widely in their implementation details. For example, I was building a workflow system, and I needed the ability to implement arbitrary tasks. To run the task, I implemented a base class that each task inherited from, with an Execute()
abstract method. The inheriting classes supplied the implementation, but the workflow system could begin the execution without knowing anything about what kind of task was being run.
Most projects aren't that clear-cut though. The second decision point is when a subset of the project has grown into a huge tangle of if-then statements or switch-case statements, and especially when those if-then statements require a lot of setup code to run correctly. I feel myself starting to lose the logic of what I'm trying to accomplish, and the code starts to feel fragile. At that point, it is usually a sign that it is time to refactor the code into base classes with specific implementations.
A big part of switching to an object-oriented style as opposed to a functional style is converting if-then statements into "run this action" statements. Instead of a huge set of if-then statements, you just tell the code to run its action. Which action is actually run depends on the implementation you supplied.
For example, here's the functional style in C#-style pseudocode:
if ( action == "email" ) {
callEmailFunction(userInfo);
}
else if ( action == "sms" ) {
callSmsFunction(userInfo);
}
else if ( action == "web" ) {
endpoint = "http://127.0.0.1/confirm";
confirmWeb(endpoint, userinfo);
}
...
But maybe you could rewrite that to something like this:
interface IConfirmable {
void Confirm(UserInfo userinfo);
}
public class ConfirmEmail : IConfirmable {
public void Confirm(UserInfo userinfo) {
// do the appropriate thing to confirm via email
}
}
public class ConfirmSms : IConfirmable {
public void Confirm(UserInfo userinfo) {
// do the appropriate thing to confirm via email
}
}
public class ConfirmWeb : IConfirmable {
// this is a constructor
public ConfirmWeb(string endpoint) {
...
}
public void Confirm(UserInfo userinfo) {
// do the appropriate thing to confirm via web
}
}
And then the code itself:
// An implementation that decides which implementation of the base class to use
// This replaces the if-then statements in the functional programmming.
IConfirmable confirmer = ConfirmerFactory.GetConfirmer();
// get the userinfo however you get it,
// which would be the same way you get it in the functional example.
UserInfo userinfo = ...;
// perform the action.
confirmer.Confirm(userinfo);
Now, when there is very little code inside the if-then, this looks like a lot of work to get no benefit. And when there is very little code in the if-then, that is correct: that's a lot of work for code that is harder to understand.
But the object-oriented style really shines when you have more than one action than just the Confirm()
method that needs to be performed. Maybe you have an initialization routine, three or more action methods that can be run, and a Cleanup()
method. The base algorithm is identical, except it makes its calls into the appropriate objects that implement a common base class. Now you start to see a real benefit to the object oriented style: the base algorithm is much easier to read than if it was checking if-then statements at every step of the way.