91

I'm having something of a hard time with designing classes in an oo way. I've read that objects expose their behavior, not their data; therefore, rather than using getter/setters to modify data, the methods of a given class should be "verbs" or actions operating on the object. For example, in an 'Account' object, we would have the methods Withdraw() and Deposit() rather than setAmount() etc. See: Why getter and setter methods are evil.

So for example, given a Customer class that keeps alot of information about the customer, e.g. Name, DOB, Tel, Address etc., how would one avoid getter/setters for getting and setting all those attributes? What 'Behavior' type method can one write to populate all that data?

IntelliData
  • 981
  • 1
  • 8
  • 15
  • 3
    possible duplicate of [Is there any reason to use "plain old data" classes?](http://programmers.stackexchange.com/questions/30297/is-there-any-reason-to-use-plain-old-data-classes) – gnat May 18 '15 at 15:29
  • 3
    see also: [When are Getters and Setters Justified](http://programmers.stackexchange.com/q/21802/31260) – gnat May 18 '15 at 15:29
  • 9
    I would point out that if you have to deal with [Java Beans specification](http://www.oracle.com/technetwork/articles/javaee/spec-136004.html), you *will* be having getters and setters. Lots of things use Java Beans (Expression Language in jsps) and trying to avoid such will likely be... challenging. –  May 18 '15 at 16:32
  • 5
    ...and, from the opposite perspective from MichaelT: if you're not using the JavaBeans spec (and I personally think you should avoid it unless you're using your object in a context where it's necessary), then there's no need for the "get" wart on getters, especially for properties that don't have any corresponding setter. I think a method called `name()` on `Customer` is as clear, or clearer, than a method called `getName()`. – Daniel Pryden May 18 '15 at 21:05
  • 2
    @Daniel Pryden: name() can mean both to set or to get?... – IntelliData May 19 '15 at 16:51
  • 1
    Why do you assume that article is nothing but 100% BS? – Andy May 19 '15 at 16:58
  • 1
    @IntelliData: As a method with no arguments, it cannot be a setter. The pattern of using methods named after the properties exists throughout the Java standard library (`Collection.size()` for example) and is quite common in other languages as well (I see it a lot in C++ code, for example). I've seen people make `name()` the getter and `name(new_name)` the setter, although I'd shy away from that in favor of having an explicit `setName()`, or better yet omitting setters entirely. IMO, value objects should always be immutable unless there's a strong need for them to be otherwise. – Daniel Pryden May 19 '15 at 18:16
  • I wonder how much of this entire conversation would be obsolete if Java had a hash literal syntax with dot property access for plain jane data structures. – Jared Smith May 20 '15 at 12:57
  • @DanielPryden: Methods are typically named using *verbs.* – Robert Harvey May 20 '15 at 13:03
  • @DanielPryden: How can a Customer be an immutable object? Don't peoples addresses & phone numbers change? – IntelliData May 20 '15 at 16:33
  • @RobertHarvey: They are indeed, and that's a good way to articulate why I don't like the `name(new_name)` form. I don't believe that naming methods after verbs is a hard *rule* though, so I have no objection to naming a (non-state-changing, obvious-from-context) method after a noun. I realize that not everyone concurs with this opinion, though. – Daniel Pryden May 20 '15 at 18:11
  • @DanielPryden: `getSomething()` is unambiguous, and enterprise Java is already exceptionally wordy anyway. – Robert Harvey May 20 '15 at 18:14
  • @IntelliData: It depends on what you're modeling `Customer` to mean. If you model `Customer` to mean "the knowledge I have about a customer at a point in time", then it can freely be immutable; when you have more knowledge you just create a different instance. In fact that can be a much easier way to model data if you have both local (in-memory) and remote (database) structures: the `Customer` object in memory will by necessity not always be identical to the `Customer` record in the database, so being able to represent them differently (and being to compare between them) can be very useful. – Daniel Pryden May 20 '15 at 18:15
  • @RobertHarvey: Are we talking exclusively about enterprise Java here, though? Like I said in my comment above, I personally think you should avoid specs like JavaBeans if you don't need them -- but if you do need them, then yeah, ignore my advice. :-) – Daniel Pryden May 20 '15 at 18:17
  • @DanielPryden the conventions that are idiomatic to a language are useful even if you are not doing "enterprise development"... – Roland Tepp May 21 '15 at 13:20
  • @RolandTepp: You're saying that naming a method `size()` or `ordinal()` rather than `getSize()` or `getOrdinal()` is not idiomatic? Better change all collection types in the standard library and the definition of enums in the language spec then, because those don't use the "get" prefix. There are plenty of other examples I can find. Basically: I reject the premise that the "get" prefix is an idiom of the *language*, only of some libraries of the language. – Daniel Pryden May 21 '15 at 15:27
  • 1
    Note that the article you mention was written in 2003 and contains (among more useful stuff) this pearl: " a metadata feature will be incorporated into Java 1.5 (due in mid 2004). [...] You'll be able to use something like: private @property int property;" which clearly didn't happen. There's [project Lombok](http://projectlombok.org/) but it does bad things to your bytecode so I wouldn't recommend it. Better use your IDE's code generation feature. – Michał Kosmulski May 23 '15 at 08:07
  • @DanielPryden I am not saying any such thing. What I was trying to say is that labeling certain idiomatic patterns as not useful just because you don't agree with them is pure hubris. Code isn't only meant for machines to execute. It's also useful to remember the people (including you five years from now) who read it later and need to understand it. Human brain is great with recognizing patterns. Naming your getters and setters in idiomatic ways helps people to recognize them for what they are. – Roland Tepp May 23 '15 at 13:49
  • @RolandTepp: I don't really want to derail the comments on this question. But my point was that omitting the "get" prefix is **also a valid idiom** in Java. Since there are multiple possible idioms which are both in wide use (see my previous comment, which you say you do not disagree with), I don't see how adopting one idiom over another -- in contexts where you have the choice -- is "pure hubris" or results in reduced code readability. You are free to disagree, and I don't begrudge you your opinion. Perhaps you should open a separate question about the naming convention? – Daniel Pryden May 23 '15 at 23:02
  • @DanielPryden, true, omitting `get` prefix is a valid pattern in Java, but if you look at how or when this is used, it usually is reserved for either computed, derived or transformed values, not so much for object attributes/properties of an object. In any case, bringing it back to the original topic, it does not matter if you put a `get` prefix in front of it or not, if it's only function is to _get a value of a private field_, it is a getter. Disguising it by omitting the `get` prefix is only going to confuse people. – Roland Tepp May 26 '15 at 06:51
  • 1
    @IntelliData Is there anything specific that you think the existing answers are lacking? Are you going to be satisfied with answers of the form "You probably want DTOs/property bags in x and y situation, but otherwise you can avoid getters and setters by...", or do you really want to be puritanical about avoiding them altogether? – Ben Aaronson May 26 '15 at 15:22
  • @Ben Aaronson: Well, the question was asked assuming the OO puritan point of view, which says that public getters/setters are almost as bad as accessing field variables, and do not implement encapsulation. According to my understanding of OOP, DTO's are discouraged as well, at least as regular entities. Most answers were attempting to justify getters/setters fir this situation, or why we might need a DTO, which is not OO pure at all... – IntelliData May 27 '15 at 16:21
  • @IntelliData DTOs are discouraged in many situations (like as domain objects), but they're very useful for transferring data across boundaries, (esp. using serialization) and this is a widely accepted practice. I don't think you're going to get any good answer that says "there are no situations where DTOs are appropriate!" simply because that isn't true. What you might get is an answer that defines a very narrow category of situations where DTOs are appropriate, and gives advice on how to more or less eliminate getters/setters elsewhere. Would that suffice? – Ben Aaronson May 27 '15 at 16:26
  • That is exactly what I meant.. Primarily with domain objects, how would I eliminate getters/setters, as in the Customer example which i gave. Whew! Somebody finally understood my question... – IntelliData May 27 '15 at 21:12
  • The easiest way to get around getters and setters is to access the data member directly. QED. – Thomas Eding May 28 '15 at 14:55
  • As the question is tagged as a Java question, please note that Java conventions require you to start your methods' names with a lower case. – mgoeminne May 30 '15 at 08:04

10 Answers10

77

The simplest way to avoid setters is to hand the values to the constructor method when you new up the object. This is also the usual pattern when you want to make an object immutable. That said, things are not always that clear in the real world.

It is true that methods should be about behavior. However, some objects, like Customer, exist primarily to hold information. Those are the kinds of objects that benefit the most from getters and setters; were there no need at all for such methods, we would simply eliminate them altogether.

Further Reading
When are Getters and Setters Justified

Robert Harvey
  • 198,589
  • 55
  • 464
  • 673
  • 3
    so why all the hype about 'Getter/Setters' are evil? – IntelliData May 18 '15 at 15:03
  • 47
    Just the usual pontification by software developers who should know better. For the article you linked, the author is using the phrase "getters and setters are evil" to get your attention, but that doesn't mean the statement is categorically true. – Robert Harvey May 18 '15 at 15:04
  • 13
    @IntelliData some people will tell you that java itself is evil. – null May 18 '15 at 15:09
  • I've heard `null` is evil. – MetaFight May 18 '15 at 16:14
  • 19
    @MetaFight `setEvil(null);` –  May 18 '15 at 16:26
  • 4
    Certainly eval is Evil Incarnate. Or a near cousin. – bishop May 18 '15 at 16:47
  • If `Customer` primarily holds information, it can be interesting to have a `Property` type and a single method `Customer.get : Property -> Value`, solving the equation `algorithms = data + code` by putting more on the `data` side and less on the `code` side. – Michaël Le Barbier May 18 '15 at 19:09
  • 1
    @IntelliData It's people who spend all their time behind ivory towers and textbooks, instead of putting good, clean code together in the real world. The rest of us understand the need to group related variables together into meaningful, yet simple, aggregates from time to time. It makes code much more maintainable than letting too many random variables float around as members in a much bigger class, just to avoid getters and setters. It also helps clean up method signatures. – Panzercrisis May 18 '15 at 20:38
  • 3
    @IntelliData It used to be the case that direct access to variables was considered evil and that all access/mutation operations should go through get/set methods. Now it's swinging the other way. I think calling getters and setters "evil" is a tongue-in-cheek reference to former conventional wisdom. – Rag May 18 '15 at 21:25
  • 1
    ... or just public final fields. Right? – user253751 May 19 '15 at 03:24
  • Data Objects are evil in a devout OOP sense, in that they separate data from behavior. But, alas, sometimes they a necessary evil. Use them when needed, but don't turn all your behavior objects into Data Objects or JavaBeans by automatically adding getters and setters for everything. – user949300 May 19 '15 at 17:16
  • 1
    @bishop `eval` can be misused, but it can be very useful for e.g. writing a REPL – Max Nanasy May 19 '15 at 17:36
  • @BrianGordon I understood the issue is that blindly following a "all variables must be accessed via a method" means people replace `myobj.var` with `myobj.getvar()` - which is fundamentally no different and why brain-dead getters and setters should be considered evil. Getting rid of these and replacing them with get/set methods that make *sense in context of the object and not the internal variables* is how accessors should be considered. – gbjbaanb May 20 '15 at 09:08
  • @IntelliData That would make an interesting question of its own (if it doesn't already exist) – Ben Aaronson May 20 '15 at 16:48
  • And then to allow you to make "modifications" while maintaining the immutability, you could have "setters" that actually return a new object that the desired property. For example, Scala's case classes have a special `copy` method that allows any number of fields to be set in this manner. So you could do something like `employee.copy(phone = "555-555-5555", email = "me@example.com")` to get a new object with the desired values. – Kat May 21 '15 at 15:24
  • @Panzercrisis: When we say 'Avoid getters/setters' we don't mean access the field variables directly; we mean access them some other way. – IntelliData May 21 '15 at 16:06
  • @Mike:The builder pattern in java is probably the closest thing to smalltalk's copy. – IntelliData May 21 '15 at 16:06
  • @IntelliData I'm not talking about exposing the variables. – Panzercrisis May 22 '15 at 03:17
62

As stated in quite a few answers and comments, DTOs are appropriate and useful in some situations, especially in transferring data across boundaries (e.g. serializing to JSON to send through a web service). For the rest of this answer, I'll more or less ignore that and talk about domain classes, and how they can be designed to minimize (if not eliminate) getters and setters, and still be useful in a large project. I also won't talk about why remove getters or setters, or when to do so, because those are questions of their own.

As an example, imagine that your project is a board game like Chess or Battleship. You might have various ways of representing this in a presentation layer (console app, web service, GUI,etc.), but you also have a core domain. One class you might have is Coordinate, representing a position on the board. The "evil" way to write it would be:

public class Coordinate
{
    public int X {get; set;}
    public int Y {get; set;}
}

(I'm going to be writing code examples in C# rather than Java, for brevity and because I'm more familiar with it. Hopefully that's not a problem. The concepts are the same and the translation should be simple.)

Removing Setters: Immutability

While public getters and setters are both potentially problematic, setters are the far more "evil" of the two. They're also usually the easier to eliminate. The process is a simple one- set the value from within the constructor. Any methods which previously mutated the object should instead return a new result. So:

public class Coordinate
{
    public int X {get; private set;}
    public int Y {get; private set;}

    public Coordinate(int x, int y)
    {
        X = x;
        Y = y;
    }
}

Note that this doesn't protect against other methods in the class mutating X and Y. To be more strictly immutable, you could use readonly (final in Java). But either way- whether you make your properties truly immutable or just prevent direct public mutation through setters- it does the trick of removing your public setters. In the vast majority of situations, this works just fine.

Removing Getters, Part 1: Designing for Behavior

The above is all well and good for setters, but in terms of getters, we actually shot ourselves in the foot before even starting. Our process was to think of what a coordinate is- the data it represents- and create a class around that. Instead, we should have started with what behavior we need from a coordinate. This process, by the way, is aided by TDD, where we only extract classes like this once we have a need for them, so we start with the desired behavior and work from there.

So let's say that the first place you found yourself needing a Coordinate was for collision detection: you wanted to check if two pieces occupy the same space on the board. Here's the "evil" way (constructors omitted for brevity):

public class Piece
{
    public Coordinate Position {get; private set;}
}

public class Coordinate
{
    public int X {get; private set;}
    public int Y {get; private set;}
}

    //...And then, inside some class
    public bool DoPiecesCollide(Piece one, Piece two)
    {
        return one.X == two.X && one.Y == two.Y;
    }

And here's the good way:

public class Piece
{
    private Coordinate _position;
    public bool CollidesWith(Piece other)
    {
        return _position.Equals(other._position);
    }
}

public class Coordinate
{
    private readonly int _x;
    private readonly int _y;
    public bool Equals(Coordinate other)
    {
        return _x == other._x && _y == other._y;
    }
}

(IEquatable implementation abbreviated for simplicity). By designing for behavior rather than modelling data, we've managed to remove our getters.

Note this is also relevant to your example. You may be using an ORM, or display customer information on a website or something, in which case some kind of Customer DTO would probably make sense. But just because your system includes customers and they are represented in the data model does not automatically mean you should have a Customer class in your domain. Maybe as you design for behavior, one will emerge, but if you want to avoid getters, don't create one pre-emptively.

Removing Getters, Part 2: External Behaviour

So the above is a good start, but sooner or later you will probably run into a situation where you have behavior which is associated with a class, which in some way depends on the class's state, but which doesn't belong on the class. This sort of behavior is what typically lives in the service layer of your application.

Taking our Coordinate example, eventually you'll want to represent your game to the user, and that might mean drawing to the screen. You might, for example, have a UI project which uses Vector2 to represent a point on the screen. But it would be inappropriate for the Coordinate class to take charge of converting from a coordinate to a point on the screen- that would be bringing all sorts of presentation concerns into your core domain. Unfortunately this type of situation is inherent in OO design.

The first option, which is very commonly chosen, is just expose the damn getters and say to hell with it. This has the advantage of simplicity. But since we're talking about avoiding getters, let's say for argument's sake we reject this one and see what other options there are.

A second option is to add some kind of .ToDTO() method on your class. This- or similar- may well be needed anyway, for example when you want to save the game you need to capture pretty much all of your state. But the difference between doing this for your services and just accessing the getter directly is more or less aesthetic. It still has just as much "evil" to it.

A third option- which I've seen advocated by Zoran Horvat in a couple of Pluralsight videos- is to use a modified version of the visitor pattern. This is a pretty unusual use and variation of the pattern and I think people's mileage will vary massively on whether it's adding complexity for no real gain or whether it's a nice compromise for the situation. The idea is essentially to use the standard visitor pattern, but have the Visit methods take the state they need as parameters, instead of the class they're visiting. Examples can be found here.

For our problem, a solution using this pattern would be:

public class Coordinate
{
    private readonly int _x;
    private readonly int _y;

    public T Transform<T>(IPositionTransformer<T> transformer)
    {
        return transformer.Transform(_x,_y);
    }
}

public interface IPositionTransformer<T>
{
    T Transform(int x, int y);
}

//This one lives in the presentation layer
public class CoordinateToVectorTransformer : IPositionTransformer<Vector2>
{
    private readonly float _tileWidth;
    private readonly float _tileHeight;
    private readonly Vector2 _topLeft;

    Vector2 Transform(int x, int y)
    {
        return _topLeft + new Vector2(_tileWidth*x + _tileHeight*y);
    }
}

As you can probably tell, _x and _y aren't really encapsulated any more. We could extract them by creating an IPositionTransformer<Tuple<int,int>> which just returns them directly. Depending on taste, you may feel this makes the entire exercise pointless.

However, with public getters, it's very easy to do things the wrong way, just pulling data out directly and using it in violation of Tell, Don't Ask. Whereas using this pattern it's actually simpler to do it the right way: when you want to create behaviour, you'll automatically start by creating a type associated with it. Violations of TDA will be very obviously smelly and probably require working around a simpler, better solution. In practice, these points make it much easier to do it the right, OO, way than the "evil" way that getters encourage.

Finally, even if it isn't initially obvious, there may in fact be ways to expose enough of what you need as behavior to avoid needing to expose state. For example, using our previous version of Coordinate whose only public member is Equals() (in practice it would need a full IEquatable implementation), you could write the following class in your presentation layer:

public class CoordinateToVectorTransformer
{
    private Dictionary<Coordinate,Vector2> _coordinatePositions;

    public CoordinateToVectorTransformer(int boardWidth, int boardHeight)
    {
        for(int x=0; x<boardWidth; x++)
        {
            for(int y=0; y<boardWidth; y++)
            {
                _coordinatePositions[new Coordinate(x,y)] = GetPosition(x,y);
            }
        }
    }

    private static Vector2 GetPosition(int x, int y)
    {
        //Some implementation goes here...
    }

    public Vector2 Transform(Coordinate coordinate)
    {
        return _coordinatePositions[coordinate];
    }
}

It turns out, perhaps surprisingly, that all the behavior we really needed from a coordinate to achieve our goal was equality checking! Of course, this solution is tailored to this problem, and makes assumptions about acceptable memory usage/performance. It's just an example that fits this particular problem domain, rather than a blueprint for a general solution.

And again, opinions will vary on whether in practice this is needless complexity. In some cases, no such solution like this might exist, or it might be prohibitively weird or complex, in which case you can revert to the above three.

Ben Aaronson
  • 6,883
  • 1
  • 30
  • 30
  • Beautifully answered! I would like to accept, but first some comments:1. I do think the toDTO() is great, bcuz ur not accessing the get/set, which allows u to change the fields given to DTO w/o breaking existing code. 2. Say Customer has enough behaviour to justify making it an entity, how would u access props to modify them, e.g. address/tel change etc. – IntelliData May 28 '15 at 17:02
  • @IntelliData 1. When you say "change the fields", you mean change the class definition or mutate the data? The latter can just be avoided by removing public setters but leaving the getters, so the dto aspect is irrelevant. The former isn't really the (whole) reason that public getters are "evil". See http://programmers.stackexchange.com/questions/157526/explanation-on-how-tell-dont-ask-is-considered-good-oo for example. – Ben Aaronson May 28 '15 at 17:58
  • @IntelliData 2. This is difficult to answer without knowing the behaviour. But probably, the answer is: you wouldn't. What behaviour could a `Customer` class have that requires being able to mutate its telephone number? Perhaps the customer's telephone number changes and I need to persist that change in the database, but none of that is the responsibility of a behaviour-providing domain object. That's a data-access concern, and would probably be handled with a DTO and, say, a repository. – Ben Aaronson May 28 '15 at 18:03
  • @IntelliData Keeping the `Customer` domain object's data relatively fresh (in sync with the db) is a matter of managing its lifecycle, which is also not its own responsibility, and would again probably end up living in a repository or a factory or an IOC container or whatever instantiates `Customer`s. – Ben Aaronson May 28 '15 at 18:03
  • I mean change class definition; even exposing getters only can be a problem: say I have several functions calling getDTO() on the object, I can always change the fields being exposed via getDTO() w/o breaking the code; however, if I do getProperty(), then if I remove the property later on I will be breaking the code in many places. – IntelliData May 28 '15 at 18:20
  • @IntelliData Yeah I understand. I agree `toDTO` it may mitigate that, but as I mentioned in the comment, there are other problems (violation of "Tell, Don't Ask"), which it does not. – Ben Aaronson May 28 '15 at 18:25
  • 1. 'Tell, Don't Ask', good point; however, if I say provideDTO(), is it in effect 'Tell' even though it returns something? (Yeah, I know it's only a semantical difference.) 2. If a behaviour providing Customer also has data, whose responsibility is it to change the data if necessary? – IntelliData May 28 '15 at 18:37
  • In general, would u say that 'Tell Don't Ask' means never return data? How is that possible? – IntelliData May 28 '15 at 18:40
  • Let us [continue this discussion in chat](http://chat.stackexchange.com/rooms/24226/discussion-between-intellidata-and-ben-aaronson). – IntelliData May 28 '15 at 18:55
  • 2
    I _really_ like the _Design for Behavior_ concept. This informs the underlying "data structure" and helps avoid the all too common anemic, hard-to-use classes. plus one. – radarbob Jun 03 '15 at 15:38
  • 1_ Where does that "IEquatable" (which you say you've abbreviated in your answer) belong to? May I know where you wanted to use this interface? 2_ Also about this line: "`return _position.Equals(other._position);`", would this be possible at all? It's accessing a private field of another object(although it's of the same class, it doesn't make a difference, private field cannot be read from an object). – aderchox Sep 06 '21 at 04:02
  • I somehow like the `toDTO()` - I'd probably call it `transfer()` - approach for use cases where you need to access a lot of fields of domain entities in order to make them cross boundaries e.g. in a HTTP API response. It's much better than accessing all the individual fields through getters and putting the data that way into the response, because a `transfer()` method is expressing the purpose very specifically and prevents careless misuse of the countless would-be-added getters on the domain entities. It's easy to spot if some yokel is abusing that method. – Eugene Nov 28 '21 at 02:12
  • ^-- After thinking some more about it I tend to belive CQRS is the correct solution to the problem mentioned in my prev comment – Eugene Nov 28 '21 at 23:05
61

It is perfectly fine to have an object which exposes data rather than behavior. We just call it a "data object". The pattern exists under names like Data Transfer Object or Value Object. If the purpose of the object is to hold data, then getters and setters are valid to access the data.

So why would someone say "getter and setter methods are evil"? You will see this a lot - someone takes a guideline which is perfectly valid in a specific context and then remove the context in order to get a more hard-hitting headline. For example "favor composition over inheritance" is a fine principle, but soon enough someone is going to remove the context and write "Why extends is evil" (hey, same author, what a coincidence!) or "inheritance is evil and must be destroyed".

If you look at the content of the article it actually have some valid points, it just stretches the point to make a click-baity headline. For example, the article states that implementation details should not be exposed. This is the principles of encapsulation and data hiding which are fundamental in OO. However, a getter method does not by definition expose implementation details. In the case of a Customer data object, the properties of Name, Address etc. are not implementation details but rather the whole purpose of the object and should be part of the public interface.

Read the continuation of the article you link to, to see how he suggest actually setting properties like 'name' and 'salary' on a 'Employee'-object without the use of the evil setters. Turns out he uses a pattern with an 'Exporter' which is populated with methods called addName, addSalary which in turn sets fields of the same name... So in the end he ends up using exactly the setter pattern, just with a different naming convention.

This is like thinking you avoid the pitfalls of singletons by renaming them therecanbeonlyonethings while keeping the same implementation.

Robert Harvey
  • 198,589
  • 55
  • 464
  • 673
JacquesB
  • 57,310
  • 21
  • 127
  • 176
  • In oop, the so called experts seem to say that this is not the case. – IntelliData May 18 '15 at 15:26
  • @IntelliData: I have amended the answer to explain why so-called experts would say such a thing. – JacquesB May 18 '15 at 16:39
  • As a matter of fact, some have explicitly said 'Never use Getter/Setters'! – IntelliData May 18 '15 at 18:54
  • 8
    Then again, some people have said 'Never use OOP': http://harmful.cat-v.org/software/OO_programming/ – JacquesB May 18 '15 at 18:57
  • True, but the question is being asked in the context of OOP... – IntelliData May 18 '15 at 19:09
  • 7
    FTR, I think more “experts” still teach to meaninglessly create getters/setters for _everything_, than to never create any at all. IMO, the latter is less misguiding advice. – leftaroundabout May 19 '15 at 07:44
  • 5
    @leftaroundabout: OK, but I'm suggesting a middle ground between 'always' and 'never' which is 'use when appropriate'. – JacquesB May 19 '15 at 09:29
  • @JacquesB A Value Object can have behavior and be more than a dumb data structure. It's an orthogonal concept. – guillaume31 May 19 '15 at 13:09
  • 4
    I think the issue is that many programmers turn every object (or too many objects) into a DTO. Sometimes they are necessary, but avoid them as much as possible since they separate data from behavior. (Assuming you are a devout OOPer) – user949300 May 19 '15 at 17:19
  • @guillaume31 Do you mean it in the DDD sense? It has historically also been used to mean the same as DTO or "property bag" (which is more C#-ish). The double-meaning is confusing which is why I think people should avoid using it for this – Ben Aaronson May 20 '15 at 16:51
  • @BenAaronson Yes, in the DDD sense which I think is the most common now. But you're right, according to [Martin Fowler](http://martinfowler.com/bliki/ValueObject.html), "Early J2EE literature used the term value object to describe a different notion, what I call a Data Transfer Object. They have since changed their usage and use the term Transfer Object instead." – guillaume31 May 22 '15 at 20:24
  • I'd say the evilness is object behavior dependent on the client manually setting state first (i.e. not using constructor or other method parameters). However, exposed in context, i.e. properly encapsulated, exposing data is not per se evil. – radarbob Jun 03 '15 at 16:04
12

To transform the Customer-class from a data object, we can ask ourselves the following questions about the data fields:

How do we want to use {data field}? Where is {data field} used? Can and should the use of {data field} be moved to the class?

E.g.:

  • What is the purpose of Customer.Name?

    Possible answers, display the name in a login web page, use the name in mailings to the customer.

    Which leads to methods:

    • Customer.FillInTemplate(…)
    • Customer.IsApplicableForMailing(…)
  • What is the purpose of Customer.DOB?

    Validating the customer's age. Discounts on the customer's birthday. Mailings.

    • Customer.IsApplicableForProduct()
    • Customer.GetPersonalDiscount()
    • Customer.IsApplicableForMailing()

Given the comments, the example object Customer – both as a data object and as "real" object with its own responsibilities – is too broad; i.e. it has too much properties/responsibilities. Which leads to either lots of components depending on Customer (by reading its properties) or to Customer depending on lots of components. Perhaps there exist different views of customer, perhaps each should have its own distinct class1:

  • The customer in the context of Account and monetary transactions is probably only used to:

    • help humans identify that their money transfer goes to the correct person; and
    • group Accounts.

    This customer does not need fields like DOB, FavouriteColour, Tel, and perhaps not even Address.

  • The customer in the context of a user logging in to a banking website.

    Relevant fields are:

    • FavouriteColour, which might come in the form of personalised theming;
    • LanguagePreferences, and
    • GreetingName

    Instead of properties with getters and setters, these might be captured in a single method:

    • PersonaliseWebPage(Template page);
  • The customer in the context of marketing and and personalised mailing.

    Here not relying on the properties of a dataobject, but instead starting from the responsibilities of the object; e.g.:

    • IsCustomerInterestedInAction(); and
    • GetPersonalDiscounts().

    The fact that this customer object has a FavouriteColour property, and/or an Address property becomes irrelevant: perhaps the implementation uses these properties; but it might also use some machine learning techniques and use previous interactions with the customer to discover in which products the customer might be interested.


1. Of course, the Customer and Account classes were examples, and for a simple example or homework exercise, splitting this customer might be overkill, but with the example of splitting, I hope to demonstrate that the method of turning a data object into an object with responsibilities will work.

Pang
  • 313
  • 4
  • 7
Kasper van den Berg
  • 2,636
  • 1
  • 16
  • 32
  • 5
    Upvote because you are actually answering the question :) However it is also obvious that the proposed solutions are much worse than just having gettes/setters - e.g. FillInTemplate clearly breaks the separation of concerns principle. Which just goes to show that the premise of the question is flawed. – JacquesB May 18 '15 at 18:48
  • @Kasper van den Berg: And when you have many attributes in the **Customer** as is usually the case, how would you initially set them? – IntelliData May 18 '15 at 19:00
  • 2
    @IntelliData your values are likely coming from a database, XML, etc. The Customer, or a CustomerBuilder, reads them, then sets using (i.e. in Java) private / package / inner class access, or, (ick) reflection. Not perfect, but you can usually avoid public setters. (See my answer for some more details) – user949300 May 18 '15 at 20:21
  • 6
    I don't think the customer class should know about any of these things. – CodesInChaos May 19 '15 at 10:13
  • What about something like `Customer.FavoriteColor`? – Gabe May 20 '15 at 07:38
  • @CodesInChaos And I think you have an anemic domain model if you think so – Eugene Jan 14 '22 at 23:33
8

TL;DR

  • Modeling for behavior is good.

  • Modeling for good(!) abstractions is better.

  • Sometimes data objects are required.


Behavior and Abstraction

There are several reasons to avoid getters and setters. One is, as you noted, to avoid modeling data. This is actually the minor reason. The bigger reason is to provide abstraction.

In your example with the bank account that is clear: A setBalance() method would be really bad because setting a balance is not what an account should be used for. The behavior of the account should abstract from its current balance as much as possible. It may take the balance into account when deciding whether to fail a withdrawal, it may give access to the current balance, but modifying interaction with a bank account should not require the user to calculate the new balance. That's what the account should do itself.

Even a pair of deposit() and withdraw() methods is not ideal to model a bank account. A better way would be to provide only one transfer() method that takes another account and an amount as arguments. This would allow the account class to trivially ensure that you don't accidentally create/destroy money in your system, it would provide a very usable abstraction, and it would actually provide users with more insight because it would force the use of special accounts for earned/invested/lost money (see double-accounting). Of course, not every use of an account needs this level of abstraction, but it is definitely worth considering how much abstraction your classes can provide.

Note that providing abstraction and hiding data internals is not always the same thing. Almost any application contains classes that are effectively just data. Tuples, dictionaries, and arrays are frequent examples. You don't want to hide the x-coordinate of a point from the user. There is very little abstraction that you can/should be doing with a point.


The Customer Class

A customer is certainly an entity in your system that should try to provide useful abstractions. For instance, it should likely be associated with a shopping cart, and the combination of the cart and the customer should allow committing a purchase, which might kick off actions like sending him the requested products, charging him money (taking into account his selected payment method), etc.

The catch is, that all the data that you mentioned is not only associated with a customer, all of that data is also mutable. The customer may move. They may change their credit card company. They may change their email-address and phone number. Heck, they may even change their name and/or sex! So, a full featured customer class indeed has to provide full modifying access to all these data items.

Still, the setters can/should provide non-trivial services: They can ensure correct format of email-adresses, verification of postal adresses, etc. Likewise, the "getters" can provide high-level services like providing email-adresses in the Name <user@server.com> format using the name fields and the deposited email address, or provide a correctly formatted postal address, etc. Of course, what of this high level functionality makes sense depends heavily on your use-case. It might be complete overkill, or it might call for another class to do it right. The choice of abstraction level is not an easy one.

6

Trying to expand on Kasper's answer, it's easiest to rant against and eliminate setters. In a rather vague, handwaving (and hopefully humorous) argument:

When would Customer.Name ever change?

Seldom. Maybe they got married. Or went into witness protection. But in that case you'd also want to check on and possibly change their residence, next of kin, and other information.

When would DOB ever change?

Only on initial creation, or on a data entry screwup. Or if they are a Domincan baseball player. :-)

These fields should not be accessible with routine, normal setters. Maybe you have a Customer.initialEntry() method, or a Customer.screwedUpHaveToChange() method that requires special permissions. But don't have a public Customer.setDOB() method.

Usually a Customer is read from a database, a REST API, some XML, whatever. Have a method Customer.readFromDB(), or, if you are stricter about SRP / separation of concerns, you'd have a separate builder, e.g. a CustomerPersister object with a read() method. Internally, they somehow set the fields (I prefer using package access or an inner class, YMMV). But again, avoid public setters.

(Addendum as Question has changed somewhat...)

Let's say that your application makes heavy use of relational databases. It would be foolish to have Customer.saveToMYSQL() or Customer.readFromMYSQL() methods. That creates undesireable coupling to a concrete, non-standard, and likely to change entity. For example, when you change the schema, or change to Postgress or Oracle.

However, IMO, it's perfectly acceptable to couple Customer to an abstract standard, ResultSet. A separate helper object (I'll call it CustomerDBHelper, which is probably a subclass of AbstractMySQLHelper) knows about all the complex connections to your DB, knows the tricky optimization details, knows the tables, query, joins, etc... (or uses a ORM like Hibernate) to generate the ResultSet. Your object talks to the ResultSet, which is an abstract standard, unlikely to change. When you change the underlying database, or change the schema, Customer doesn't change, but the CustomerDBHelper does. If you are lucky, it's only AbstractMySQLHelper that changeswhich automatically makes the changes for Customer, Merchant, Shipping etc...

This way you can (perhaps) avoid or lessen the need for getters and setters.

And, the main point of the Holub article, compare and contrast the above to how it would be if you used getters and setters for everything and changed the database.

Similarly, let's say you use a lot of XML. IMO, it's fine to couple your Customer to an abstract standard, such as a Python xml.etree.ElementTree or a Java org.w3c.dom.Element. Customer gets and sets itself from that. Again, you can (perhaps) lessen the need for getters and setters.

user949300
  • 8,679
  • 2
  • 26
  • 35
  • would you say it is advisable to use a Builder Pattern? – IntelliData May 19 '15 at 16:55
  • Builder is useful for making the construction of an object easier and more robust, and, if you wish, allowing the object to be immutable. However, it still (partially) exposes the fact that the underlying object has a DOB field, so it's not the bee-all end-all. – user949300 May 19 '15 at 17:10
1

So for example, given a Customer class that keeps alot if information about the customer, e.g. Name, DOB, Tel, Address etc., how would one avoid getter/setters for getting and setting all those attributes? What 'Behavior' type method can one write to populate all that data?

I think this question is prickly because you are worried about behavior methods for populating data but I don't see any indication of what behavior the Customer class of objects is intended to encapsulate.

Don't confuse Customer as a class of objects with 'Customer' as a user/actor who performs different tasks using your software.

When you say given a Customer class that keeps alot if information about the customer then as far as behavior goes it looks like your Customer class has little distinguishing it from a rock. A Rock can have a color, you could give it a name, you could have a field for storing its current address but we don't expect any sort of intelligent behavior from a rock.

From the linked article about getters/setters being evil:

The OO design process centers on use cases: a user performs standalone tasks that have some useful outcome. (Logging on is not a use case because it lacks a useful outcome in the problem domain. Drawing a paycheck is a use case.) An OO system, then, implements the activities needed to play out the various scenarios that comprise a use case.

Without any behavior defined, referring to a rock as a Customer doesn't change the fact that it is just an object with some properties you'd like to track and it doesn't matter what tricks you want to play to get away from getters and setters. A rock doesn't care if it has a valid name and a rock wouldn't be expected to know whether an address is valid or not.

Your order system could associate a Rock with a purchase order and as long as that Rock has an address defined then some part of the system can make sure an item gets delivered to a rock.

In all of these cases the Rock is just a Data Object and will continue to be one until we define specific behaviors with useful outcomes instead of hypotheticals.


Try This:

When you avoid overloading the word 'Customer' with 2 potentially different meanings it should make things easier to conceptualize.

Does a Rock object place an Order or is that something that a human being does by clicking on UI elements to trigger actions in your system?

nvuono
  • 151
  • 4
  • The customer is an actor that does many things, but also happens to have alot of info associated with it; does that justify creating 2 separate classes, 1 as an actor and 1 as a data object? – IntelliData May 19 '15 at 16:59
  • @IntelliData passing rich objects across layers is where things get tricky. If you are sending the object from a controller to a view, the view needs to understand the general contract of the object (JavaBeans standard for example). If you are sending the object across the wire, JaxB or the like needs to be able to reinstaitate it to a dumb object (because you didn't give them the full rich object). Rich objects are wonderful for manipulating data - they are poor for transferring state. Conversely, dumb objects are poor for manipulating data and ok for transferring state. –  May 19 '15 at 19:49
  • +1 It's not answering the question 100 %, but anyway it is an important hint that "customer" *might* be a too generic term – Eugene Jan 14 '22 at 23:45
1

The issue of having getters and setters can be a matter of the fact that a class may be used in the business logic in one way but you may also have helper classes to serialize / deserialize the data from a database or file or other persistent storage.

Due to the fact there are many ways to store / retrieve your data and you want to decouple the data objects from the way they are stored, the encapsulation can be "compromised" by either making these members public, or making them accessible through getters and setters which is almost as bad as making them public.

There are various ways around this. One way is to make the data available to a "friend". Although friendship is not inherited, this can be overcome by whatever serializer requesting the information from the friend, i.e. the base serializer "forwarding" the information.

Your class could have a generic "fromMetadata" or "toMetadata" method. From-metadata constructs an object so may well be a constructor. If it is dynamically typed language, metadata is pretty standard to such a language and probably is the primary way to construct such objects.

If your language is C++ specifically, one way around this is to have a public "struct" of data and then for your class to have an instance of this "struct" as a member and in fact all the data you are going to store / retrieve to be stored in it. You can then easily write "wrappers" to read/write your data in multiple formats.

If your language is C# or Java which don't have "structs" then you can do similarly but your struct is now a secondary class. There is no real concept of "ownership" of the data or const-ness so if you give out an instance of the class containing your data and it's all public, whatever gets hold can modify it. You could "clone" it although this can be expensive. Alternatively you could make this class have private data but use accessors. That gives users of your class a roundabout way to get to the data but it isn't the direct interface with your class and is really a detail in storing the data of the class which is a use-case too.

CashCow
  • 1,610
  • 13
  • 16
0

OOP is about encapsulating and hidding behavior inside objects. Objects are black boxes. This is a way to design thing. The asset is in many case one doesn't need to know the internal state of another component and is better to not have to know it. You can enforce that idea with mainly interfaces or inside an object with visibility and taking care only allowed verbs/action are available to caller.

This work well for some kind of problem. For example in user interfaces to modelize individual UI component. When you interract with a text box you are only interrested in setting the text, getting it or listening to text change event. You are typically not interrested as to where the cursor is, the font used to draw the text or how the keyboard is used. Encapsulation provide a lot here.

On the contrary when you call a network service, your provide an explicit input. There usually a grammar (like in JSON or XML) and all the option of calling the service have no reason to be hidden. The idea is that you can call the service the way you want and the data format is public and published.

In this case, or many other (like access to a database) you really work with shared data. As such there no reason to hide it, on the contrary you want to make it available. There can be concern of read/write access or datacheck consistency but at this core, the core concept if this is public.

For such design requirement where you want to avoid encapsulation and make things publics and in the clear, you want to avoid objects. What you really need are tuples, C structs or their equivalent, not objects.

But it also happen in languages like Java, the only things you can modelize is objects or arrays of objects. Objects themselve can hold a few natives types (int, float...) but that's all. But objects can also behave like a simple struct with just public fields and that all.

So if you modelize data you can be done with just public fields inside objects because you don't need more. You don't use encapsulation because you don't need it. This is done this way in many languages. In java, historically, a standard rose where with getter/setter you could at least have read/write control (by not adding setter for example) and that toolings and framework by using instrospection API would look for getter/setter methods and use it to autofill the content or display theses as modifiable fields in auto generated user interface.

There also the argument you could add some logic/checking in the setter method.

In reality there almost no justification for getter/setters as they are most often used to modelize pure data. Frameworks and developpers using your objects do expect the getter/setter do nothing more than setting/getting the fields anyway. You are effectively doing no more with getter/setter than what could be done with public fields.

But that's old habits and olds habits are difficult to remove... You could even be threatened by your collegues or teacher if you don't put getters/setter blindly everywhere if they lack the background to better understand what they are and what they are not.

You would likely need to change the language to get ride of all theses getters/setters boilerplate code. (Like C# or lisp). To me getters/setter are just another one billion dollar mistake...

  • 6
    C# properties do not really implement encapsulation any more than do getters and setters... – IntelliData May 19 '15 at 17:01
  • 1
    The advantage of [gs]etters is that you can do the things you usually don't (check values, notify observers), simulate non-existing fields etc., and mainly that you can *change it later*. With [Lombok](https://projectlombok.org), the boilerplate is gone: `@Getter @Setter class MutablePoint3D {private int x, y, z;}`. – maaartinus May 20 '15 at 03:47
  • 1
    @maaartinus For sure [gs]etter can do anything as any other method can. This mean that any caller code should be aware any value they set can throw an exception, be changed or potentialy send notification of change... or whatever else. That more or less [gs]etter are not providing access to a field but doing arbitrar code. – Nicolas Bousquet May 20 '15 at 07:06
  • @IntelliData C# properties allow to not write 1 useless single character of boilerplate and to not care of all this getter/setter stuff... This is already better achievement than project lombok. Also to me a POJO with just getters/setter is not here to provide encapsulation but rather to publish a data format that one is free to read or write as exchange with a service. Encapsulation is then the opposite of the design requirement. – Nicolas Bousquet May 20 '15 at 07:17
  • I don't really think properties are really good. Sure, you save the prefix and the parentheses, i.e., 5 chars per call, but 1. they look like fields, which is confusing. 2. they're an additional thing for which you need support in reflection. No big deal, but no big advantage either (when compared to Java+Lombok; pure Java is at clear loss). – maaartinus May 20 '15 at 07:51
0

I add my 2 cents here mentioning SQL-speaking objects approach.

This approach is based on the notion of self-contained object. It has all resources it needs to implement its behavior. It doesn't need to be told how to do its job -- declarative request is enough. And an object definitely doesn't have to hold all its data as class properties. It really doesn't -- and shouldn't -- matter where they are got from.

Talking about an aggregate, immutability is not an issue as well. Say, you have a sequence of states that aggregate can hold: Aggregate root as saga It's totally fine to implement each state as standalone object. Probably you could go even further: have a chat with your domain expert. Chances are that he or she doesn't see this aggregate as some unified entity. Probably each state has it's own meaning, deserving it's own object.

Finally, I'd like to note that object finding process is very similar with system decomposition into subsystems. Both are based on behavior, not anything else.

Vadim Samokhin
  • 2,158
  • 1
  • 12
  • 17