As an Agile Coach, Miško is responsible for teaching his co-workers to maintain the highest level of automated testing culture, allowing frequent releases of applications with high quality. He is very involved in Open Source community and an author of several open source projects. Recently his interest in Test Driven Developement turned into http://TestabilityExplorer.org with which he hopes will change the testing culture of the open source community. Misko is a DZone MVB and is not an employee of DZone and has posted 38 posts at DZone. You can read more from them at their website. View Full User Profile

How To Think About OO

08.03.2009
| 6704 views |
  • submit to reddit

Everyone seems  to think that they are writing OO after all they are using OO languages such as Java, Python or Ruby. But if you exam the code it is often procedural in nature.

Static Methods

Static methods are procedural in nature and they have no place in OO world. I can already hear the screams, so let me explain why, but first we need to agree that global variables and state is evil. If you agree with previous statement than for a static method to do something interesting it needs to have some arguments, otherwise it will always return a constant. Call to a staticMethod() must always return the same thing, if there is no global state. (Time and random, has global state, so that does not count and object instantiation may have different instance but the object graph will be wired the same way.)

This means that for a static method to do something interesting it needs to have arguments. But in that case I will argue that the method simply belongs on one of its arguments. Example: Math.abs(-3) should really be -3.abs(). Now that does not imply that -3 needs to be object, only that the compiler needs to do the magic on my behalf, which BTW, Ruby got right. If you have multiple arguments you should choose the argument with which method interacts the most.

But most justifications for static methods argue that they are “utility methods”. Let’s say that you want to have toCamelCase() method to convert string “my_workspace” to “myWorkspace”. Most developers will solve this as StringUtil.toCamelCase(”my_workspace”). But, again, I am going to argue that the method simply belongs to the String class and should be “my_workspace”.toCamelCase(). But we can’t extend the String class in Java, so we are stuck, but in many other OO languages you can add methods to existing classes.

In the end I am sometimes (handful of times per year) forced to write static methods due to limitation of the language. But that is a rare event since static methods are death to testability. What I do find, is that in most projects static methods are rampant.

Instance Methods

So you got rid of all of your static methods but your codes still is procedural. OO says that code and data live together. So when one looks at code one can judge how OO it is without understanding what the code does, simply by looking at the relationship of data and code.

class Database {
  // some fields declared here
  boolean isDirty(Cache cache, Object obj) {
    for (Object cachedObj : cache.getObjects) {
      if (cachedObj.equals(obj))
        return false;
    }
    return true;
  }
}

The problem here is the method may as well be static! It is in the wrong place, and you can tell this because it does not interact with any of the data in the Database, instead it interacts with the data in cache which it fetches by calling the getObjects() method. My guess is that this method belongs to one of its arguments most likely Cache. If you move it to Cache you well notice that the Cache will no longer need the getObjects() method since the for loop can access the internal state of the Cache directly. Hey, we simplified the code (moved one method, deleted one method) and we have made Demeter happy.

The funny thing about the getter methods is that it usually means that the code where the data is processed is outside of the class which has the data. In other words the code and data are not together.

class Authenticator {
  Ldap ldap;
  Cookie login(User user) {
    if (user.isSuperUser()) {
      if ( ldap.auth(user.getUser(),
             user.getPassword()) )
        return new Cookie(user.getActingAsUser());
    } else (user.isAgent) {
        return new Cookie(user.getActingAsUser());
    } else {
      if ( ldap.auth(user.getUser(),
             user.getPassword()) )
        return new Cookie(user.getUser());
    }
    return null;
  }
}

Now I don’t know if this code is well written or not, but I do know that the login() method has a very high affinity to user. It interacts with the user a lot more than it interacts with its own state. Except it does not interact with user, it uses it as a dumb storage for data. Again, code lives with data is being violated. I believe that the method should be on the object with which it interacts the most, in this case on User. So lets have a look:

class User {
  String user;
  String password;
  boolean isAgent;
  boolean isSuperUser;
  String actingAsUser;

  Cookie login(Ldap ldap) {
    if (isSuperUser) {
      if ( ldap.auth(user, password) )
        return new Cookie(actingAsUser);
    } else (user.isAgent) {
        return new Cookie(actingAsUser);
    } else {
      if ( ldap.auth(user, password) )
        return new Cookie(user);
    }
    return null;
  }
}

Ok we are making progress, notice how the need for all of the getters has disappeared, (and in this simplified example the need for the Authenticator class disappears) but there is still something wrong. The ifs branch on internal state of the object. My guess is that this code-base is riddled with if (user.isSuperUser()). The issue is that if you add a new flag you have to remember to change all of the ifs which are dispersed all over the code-base. Whenever I see If or switch on a flag I can almost always know that polymorphism is in order.

class User {
  String user;
  String password;

  Cookie login(Ldap ldap) {
    if ( ldap.auth(user, password) )
      return new Cookie(user);
    return null;
  }
}

class SuperUser extends User {
  String actingAsUser;

  Cookie login(Ldap ldap) {
    if ( ldap.auth(user, password) )
      return new Cookie(actingAsUser);
    return null;
  }
}

class AgentUser extends User {
  String actingAsUser;

  Cookie login(Ldap ldap) {
    return new Cookie(actingAsUser);
  }
}

Now that we took advantage of polymorphism, each different kind of user knows how to log in and we can easily add new kind of user type to the system. Also notice how the user no longer has all of the flag fields which were controlling the ifs to give the user different behavior. The ifs and flags have disappeared.

Now this begs the question: should the User know about the Ldap? There are actually two questions in there. 1) should User have a field reference to Ldap? and 2) should User have compile time dependency on Ldap?

Should User have a field reference to Ldap? The answer is no, because you may want to serialize the user to database but you don’t want to serialize the Ldap. See here.

Should User have compile time dependency on Ldap? This is more complicated, but in general the answer depends on wether or not you are planning on reusing the User on a different project, since compile time dependencies are transitive in strongly typed languages. My experience is that everyone always writes code that one day they will reuse it, but that day never comes, and when it does, usually the code is entangled in other ways anyway, so code reuse after the fact just does not happen. (developing a library is different since code reuse is an explicit goal.) My point is that a lot of people pay the price of “what if” but never get any benefit out of it. Therefore don’t worry abut it and make the User depend on Ldap.

From http://misko.hevery.com

Published at DZone with permission of Misko Hevery, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Artur Sobierajczyk replied on Mon, 2009/08/03 - 12:19pm

I strongly disagree with the design you are forcing to use. Putting many methods into classes results in a nightmare, very long classes with unrelated methods. All methods use the private state so it is risky to change the implementation. A much better solution is to have in a class as few as possible methods to give good interface to the data. You can have then many modules (possibly related) that operate on that class. So you can have a Math module for Double type and a MetricCalculations module. Inserting recalculations from kilograms to pounds in number type, as is done in Ruby, is a real anti-pattern for me!

My view isn't anti-OO. This is how Bjarne Stroustrup views the problem!

Your view of OO is a very "religious" - in 90s it could be accepted but now I don't see a place for such view. OO definitely isn't a silver ball. And look what people want now in programming languages - things like closures, generics, first order functions. This is definitely not a OO. I also think that OO is a part of low-level design. What really matters in big software is a modularity of higher level components and this is not a OO-thing.

 I don't also understand why you find static methods not testable. I think they are more testable than classes, because they clearly define it's input and output - by it's arguments and return value. In classes you have state, so the number of things a method depends on is much bigger.

Sorry if you find my post offensive - I'm simply tired of over-engineered OO-design.

Developer Dude replied on Mon, 2009/08/03 - 3:50pm

Sometimes good design/architecture in OOP is less about being true to some ideal of where a method should be that performs some logic involving an object, and more about the advantages one approach gives you over another.

For example, instead of making User all knowing and all powerful (or more knowing and more powerful) with regards to typical user operations (such as login, authentication, authorization), it *might* be better to have user be a simple value object that holds a few key pieces of data (name, password, contact info, maybe preferences, maybe authorities) and instead have a different object that will use the data in user to perform the logic of authentication/authorization/etc.

Why? Because there are a lot more types of authentication/authorization strategies/mechanisms/implementations/configurations (against a simple datastore, LDAP, Active Directory, CAS, etc.) than there are types of users. Generally it makes more sense to put that kind of logic behind an authentication/authorization interface (usually a provider) and use a factory and/or IOC and/or some kind of pluggable/configurable architecture rather than build assumptions into the user (for example, a Swing app user might not have a cookie - especially if it never talked to a server).

I have a general rule: 'never say never'. The right tool for the right job. I try to not get too rigid about where something (behavior, data) belongs because the context can change. I also try to stay flexible about when to use OOD/OOP v. Aspect Oriented Programming v. DDD v. procedural logic v. whatever other tool fits the need. I also try to design for reuse and generality. In my opinion it is often easier to do that when you have your service logic separate from your data.

Sometimes static methods are the right tool, sometimes not. Sometimes an object/class should only be a simple value object with only state, getters/setters and quite possibly an overriding implementation of equals() and hashCode(). Sometimes an object/class needs a lot of logic. Then there is the idea of wrapping a value object with an aspect oriented proxy.

I tend to think of almost any code I write fitting into one or more parts of an SVM architecture: Service, where there is a lot of algorithms, business logic, service logic, utility logic that operates on data. The View, which is usually a UI (doesn't have to be). The Model, where the data often resides - this can be a persisted or temporary model (shouldn't usually matter to the Service or View).

Sure, maybe some static methods should not be static - that doesn't mean they should be avoided at all costs. As for String, we can't extend it not because it isn't OO, but rather because it was declared final - a trade off of the way the designers made it immutable which has its own advantages and disadvantages.

Danny Graham replied on Mon, 2009/08/03 - 3:59pm

I completely agree with keeping the code with the data - but your in User manager example - there is no way User should know about Ldap - and worse Cookie - this means that every part of the system needs to know about Cookie and Ldap -  so the middle tier (say), is suddenly required to know about Ldap and Cookies - presentation tier artifacts. 

A general caveat to the "code with data" is in the case of simple objects like user - these should be kept simple - they are effectively data transfer objects (dto's) they really shouldn't know a great deal about the outside world at all. 

Finally - no mention of interfaces... this is one of the worst sins of the procedural OO people - it's all just objects with methods, with, as you identify in your post,  "code with data", while keeping this clean, and keeping the right code with the right data, and obeying Demeter is fine - unless you program to interfaces, you are still closely coupling your objects to full blown implementations of the operations they require - and this will require extensive mocking in your tests cases.

Gilbert Herschberger replied on Mon, 2009/08/03 - 10:27pm

What happened to our noble quest for object oriented programming? Are we going to abandon our pursuit of pure OO in a post-Java era? I appreciate your article. You have reminded me about what object oriented programming should be, and what popular Java programming is sometimes not.

Pure OO requires us to use aggregation to create a complex object, such as an application. But aggregation seems abandoned in favor of static factory methods, a simpler and less flexible pattern. The benefit of aggregation is lost; yet, the need for aggregation remains. What a waste of effort when programmers go off in search of a new solution when aggregation is already known to be the best answer.

It is not possible for a module system for Java, such as OSGi, to be constructed from a large collection of static methods. The premise of a module system is aggregation. An application is a complex object, an aggregation of its modules. To build a module, one must minimize the number of static methods.

Code and data should be as closely coupled as possible, encapsulated in an object. A collection of objects should be encapsulated in a module. Now where exactly does a static method fit into a pure OO picture?

Java is intended to be practical OO, not pure. It is intended for work outside of a research lab. It supports both OO and non-OO together at the same time. It can be more or less OO according to your taste.

Why is a Java application required to depend upon at least one static method called main? This is not a requirement of the JVM. It was invented to appeal to non-OO programmers. Other than that arbitrary requirement, how many static methods do we need? They are shortcuts with a few very negative consequences. Therefore, I agree that we should think long and hard before we write yet another static method.

Unfortunately, your example is distracting. A general-purpose User object should not include any code for authentication, as if there were only one mechanism. In fact, a pure User object should not include any credentials at all, as if user ID and password were the only kind of credential. With pure OO, Credentials is one object, an Authenticator is another. Then, User represents a relationship between these two. A User is an aggregate of Credentials and Authenticator. And then, User.authenticate() makes sense.

Mark Haniford replied on Mon, 2009/08/03 - 9:36pm

Good comments.  I hope we can more discussion on the post OO world.

 By the way in C#, you would just write an extension method on string:

namespace Company.Com.StringExtensions
{
    public static class ExtendString
    {
        public static string CamelCase(this String str, char camelerChar)
        {
            var stringBuilder = new StringBuilder();
            for (var i = 0; i < str.Length - 1; i++ )
            {
                if (str[i] == camelerChar)
                {
                    stringBuilder.Append(Char.ToUpper(str[i + 1]));
                    i += 1;
                }
                else
                {
                    stringBuilder.Append(str[i]);
                }
            }
            return stringBuilder.ToString();
        }
    }
}

 

Yeah, that code sucks and doesn't deal with corner cases, and in a "real" language we would use map and filter to do it right, but you get the point

The point being you only get your string pollution when you bring in the namespace

 

I really appreciated artwar's comments.  I think we're entering the post-Java OO dogma era.

 

I'm all for anemic classes that for the most part inforce invariants, with services classes that do the grunt work.

 

But for the op, I agree when possible we should follow demeter's law.  getters and setters are a code smell, but sometimes we don't have control over the objects we use.

 

Kirk Pepperdine replied on Tue, 2009/08/04 - 12:40am

http://www.antiifcampaign.com/

Mladen Girazovski replied on Tue, 2009/08/04 - 4:34am

Misko, i agree with most of your points, however, i'd prefer to put the whole Authentification into a (User-)Repository, including the dependency to LDAP or any other DB technology.

 

 My view isn't anti-OO. This is how Bjarne Stroustrup views the problem!

Oh well, Bjarne couldn't even get one OO language right, C++ ist far from being a real OO language, i don't think his opinion is important when it comes to OO Design.

 

Tracy Nelson replied on Thu, 2009/08/06 - 12:31pm

Some good points, although I'm disappointed you didn't mention responsibility as one of the guiding forces behind OOP.  The way you determine what code goes with which data depends on what an object's responsibilites are. I think that Gilbert got it almost right, but I'd suggest that a User does have Credentials, and that the Cookie is the representative of the successful authorization.  This permits things other than Users to be authorized (e.g., other systems or services) and subclassing Cookie also permits easy modeling of access levels (e.g., AppUserCookie and AppAdminCookie).  This explicitly models access control, which is what we're concerned with.

Atwar's response reinforces my belief that too many developers haven't been exposed to good examples of OO, and therefore don't recognize the benefits.  Large objects with lots of internal state isn't OOP, it's FORTRAN (which, as we all know, can be written in any language).  I think the problem is that FORTRAN demands a consistent level of effort to use, whereas OOP demands higher effort up front (in design) with the payback coming on the back end (easier debugging and reuse).  Unfortunately, if you don't really "get" OOD, or you skimp on the design, then you wind up with a consistently higher level of effort, which eventually leads to the "FORTRAN modules" mentality.

Developer Dude replied on Fri, 2009/08/07 - 11:19am

A cookie may be a container for authorization data, but it is not representative of all authorization implementations, only a few. If you look at the User/UserDetails interfaces in Spring Security, you see:

Name

Password

Authorities (typically an array of strings that describe the authority role, but also an interface).

A few booleans (expired, etc.)

That's it. No cookie.

My point is that good design takes into account reuse and that often this means separating the data from the behavior, and programming to a minimal interface.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.