Avoid Direct Access Of Members

In many places I have seen the following.

    public void doSomething() {
        this.member_ = "Some value";
        ...
        ...
    }

When there is already a getter/setter for this member. This causes problems while extending the functionality of the class. Therefore, when you have to access a member variable, do the following.

    public void doSomething() {
        setMember("Some value");
        ...
        ...
    }

This enables the inherited classes to override the "setMember" and do some additional tasks. Also, this way some additional checks can be easily added later without breaking the existing code.

-- vhi


What about AspectJ (AspectJay), where you can override even this.member = "Some Value"

DoTheSimplestThingThatCouldPossiblyWork: unless you are having problems with cross-cutting concerns, AspectJ is a lot of work for something that is handled nicely by inheritance.

DoTheSimplestThingThatCouldPossiblyWork: Having the code in a class depend on the class' own implementation strategy is not that bad of a CodeSmell. Just go ahead and use the member variables until such a time as you find that you need to redesign the implementation, then change all member access to accessor functions.

Anyway, AccessorsAreEvil. ;->


The SelfLanguage does not expose member variables (slots) to the programmer. All access to slots is done through accessors. Declaring a slot defines the accessors in some implementation-dependent manner. E.g. declaring a slot called "slot" would define a getter called "slot" and a setter called "slot: new_value".

RubyLanguage has something similar with its accessors, but also allows direct member access from within the object.


Using your own variables (outside of accessors) is one thing. Using your parent class' variables is truly evil.

I spent four days once (intermittently, in between doing real work) tracking down a bug that was driving me nuts. A member variable kept getting changed in the production system, and I couldn't see how. Putting a breakpoint at the setter didn't show me anything unexpected. A line-by-line inspection of the code didn't help me either. Then, all of a sudden, I noticed that the variable wasn't declared as private, but as protected. When I changed this, my jaw dropped as the approximately 500 compile errors this caused creeped out of the woodwork (about 120 of these were value changing!)

Sometimes, good coding habits (like making your non-final variables private) can really leave you blindsided to the evil practices that others use.

You can discover all sorts of interesting things about a program by simply commenting out a variable...


One could argue that Java is a flawed language and ideally one should be able to hide the distinction between changing variables and using an accessor. This can simplify syntax and allow one to swap one for the other. If an accessor is simply a formal wrapper around a variable, then by itself it's not providing any value and is just repetitious bloat that hogs eye real-estate. It only provides value if we LATER want to add more processing or control. Thus, it's more logical to start out with direct variables, but change them to accessors later if and when needed. But with Java you'd have to change the interface to do this. In a "proper" language, you wouldn't: you could switch to accesors from variables without changing existing calling code. A side-effect of this is that you wouldn't be able to tell whether it's a direct reference or an accessor by looking at the call interface alone, unless perhaps you make it "read-only" or add parameters. (Somewhere there is a related existing topic on this.)

But if one is stuck with a language that forces an external extinction, like Java, I would suggest leaving variables "naked" if there will be relatively few users (other classes) of the class and these associations will be relatively stable. If, however, there will be a lot of using classes and/or the associations change fairly often, then go ahead and wrap them in accessors up front. -t

-t

Take a look at C#'s Properties, and please reflect on whether your advice is based on good practice or your personal preference. There are good reasons -- that you've not mentioned -- not to do what you suggest.

Let's stick with Java for now. I'll come back to C# later. Per Java, it's to avoid bloat. One only should create such bloat if the cost of change is great enough to justify it. Bloat creates risk by confusing and/or distracting "the eye", but visiting many callers to convert direct attributes to accessors when later needed also creates risk (and work). Thus, there is a balancing point of risk. If you only are likely to have a few and stable callers, then the bloat doesn't prevent enough risk & rework to justify it's own risk (by being bloat). Granted, I have no formal studies on confusion/distraction caused by bloat, and these are based on my personal observations of my own work and others'.

General illustration of trade-offs:

Scenario A: 2 Callers

Scenario B: 40 Callers

Different people may assign different values to the 1) cost of bloat (see FastEyes), 2) the probability that we'll later need accessors, and 3) the cost of changing the interface (and changing the callers). But do notice the "c * 40" (c times 40) in B.1. It's probably a high total by most scenarios (readers applying their estimated costs). If we don't wrap and have few callers (A.1), then the cost of changing the interface is relatively low, and arguably lower than the cost of bloat (A.2). I believe most would agree under that scenario it's probably a good idea to wrap, but the "best" choice in scenario A is probably subject to heavy debate, based on estimations of one's own WetWare and/or that of typical developers in their shop. It's roughly a wash by my estimate, leaning toward A.1 in the name of YagNi. Related: DecisionMathAndYagni, SimulationOfTheFuture.

Do you feel that's a balanced assessment? The only reason you mention to avoid direct access of member variables is that the interface might change.

I am not following. Please elaborate, perhaps with a specific example.

It's not an issue of a specific example, but of the fact that your scenarios are focused almost entirely on "bloat" and "visual complexity". There are good reasons to avoid direct access of member variables, such as to increase encapsulation and reduce coupling. Why do you not mention these?

"Coupling" is an ill-defined concept, and encapsulation for encapsulation's sake is a waste of code. I am not suggesting that one don't wrap/hide variables, but rather only do it WHEN there is known and existing reason, or if there is likely to be one in the future. Thus, I am not arguing against encapsulation in general. It's a matter of if and when to encapsulate. I'm not a YagNi purist in that I append "likely to need" in my criteria versus "until actually needed" in pure YagNi.

Coupling is a very well-defined ComputerScience/SoftwareEngineering concept. Coupling exists wherever there is a dependency such that altering A affects B. In that case, we say that A and B are coupled. Where coupling is necessary, we try to group it together whenever possible. That's cohesion. To reduce accidental or intentional (but unnecessary) coupling between defined units of cohesive code, we use encapsulation. Your argument appears to be based on the presumption that code will be static and neither re-used nor modified, and is simple enough that accidental coupling is unlikely.

Also, lack of encapsulation can create "accidents", but so can bloated code. If the accidents caused by bloat exceed those caused by lack of encapsulation, then encapsulation is not giving us a net benefit. Bloat can also slow general productivity by making more code to read and change.

What is "encapsulation for encapsulation's sake"? Isn't encapsulation for reducing coupling and increasing cohesion?

It depends. See above.

Encapsulation always reduces coupling and increases cohesion. What you appear to believe "depends" is whether it's worth the additional code of get/set/etc. or not.

Without a clear definition/metric of "coupling" and "cohesion", I cannot confirm nor deny that claim. But this is NOT the topic to define/debate coupling and cohesion, as a reminder.

I've given a clear definition of CouplingAndCohesion. They are abstract and often qualitative (though specific quantitative metrics may be defined for specific cases, but not in general), but that doesn't mean they're vague.

If there are no consensus numeric or Boolean metrics for it, or a definition clear enough to lead to that, then it's "vague" in my book. The existing proposals have too much dependency on damned English, and we know how that turns out.

Many things have no established numeric or boolean metric, and yet they're clear enough to make decisions. For example, "programming language" has no established metric, and yet millions of people use them and create them every day.

Many successfully use YagNi also.

YagNi is about only implementing requirements that you need to implement. It isn't advice to write what would generally be considered bad code that violates encapsulation.

That's your opinion. Again, encapsulating before encapsulation is actually needed can indeed be interpreted as a violation of YagNi. But I don't want to make this into a "principle war" but rather explore the ACTUAL costs versus benefits with something more concrete.

That sounds like a highly nuanced and personal interpretation of YagNi. I'd be curious to see if ExtremeProgramming, or any other Agile methodology that endorses YagNi, advocates it.

As long as they don't rely on ArgumentFromAuthority, I would indeed like to see wider opinions also.
Map Perspective

I consider objects to be "glorified maps" (dictionary structures). It's acceptable to have maps without wrapping each element of a map. If we say "always wrap" all "public" elements of an object, then as soon as we add a single method to the map, we would then be obligated to wrap every element, creating an all-or-nothing DiscontinuitySpike. It's like that one method is a poison pill that suddenly triggers some grand encapsulation rule. The boundary between "map" and "object" can and should be fuzzy: I see no reason to hard-classify them (forced dichotomy). See also MergingMapsAndObjects. -t

That also a nuanced and personal interpretation of ObjectOriented programming. OO is defined by encapsulation, inheritance, and polymorphism, not that objects are like maps.

Encapsulation is NOT a hard requirement of OOP. And NobodyAgreesOnWhatOoIs. Thus, my preferred viewpoint of OO is not less valid than others. And RobertMartin's "jump table" definition/description of OOP can be viewed the same or similar to a map view. A "jump table" is simply a map from name/key to a function (implementation and/or reference to).

Are you referring to the exchange at http://objectmix.com/object/312321-object-oriented-programming-explained-51-lines-6.html ?

The debates over OO definitions fall into established categories. Your "preferred viewpoint" appears to be unique to you, and not in any of the established categories.

That's fine, I'm sticking by it even if it is a minority opinion. In practice I do see maps morph into objects as more requirements are added (at least for languages that help blur the distinction).

Could you provide an example of what you mean by "maps morph into objects as more requirements are added"?

One starts with a typical record "structure" (AKA map), and various actions seem to group naturally with that "structure". The map may still be used in map-ish ways, but we now have methods that are specific to that structure. For example, it might be app configuration info that is typically only set by field technicians. We later want a "list_to_screen" method for it to display it for easier trouble-shooting by field technicians (using a "hidden" back-room UI).

By map, do you mean a collection of named elements, aka a dictionary? I don't know what your field technician example is intended to illustrate, but it's trivially obvious that operations may be defined to manipulate a given structure. That doesn't make it object oriented, only that you've defined a structure and a set of associated operations.

That gets back to how one defines "object oriented". I don't define it by "wrap-ness" or "encapsulation" level. I'd argue that full wrapping is really creation of an AbstractDataType and not (just) object orientation. OO is wider than ADT. You appear to be conflating the two. If they are one in the same, then we should dispense with the term "object oriented".

An AbstractDataType is a mathematical model for a category of data structures. It is isomorphic to certain applications of object oriented programming, but not equivalent. Particularly, they are not the same because AbstractDataType is not defined in terms of polymorphism, inheritance, or encapsulation. Other OO definitions are either too amorphous or too individuated to consider.

That may depend on how one defines polymorphism, inheritance, or encapsulation (the "big 3" for reference). Anyhow, it's reasonable that one may wish to use one or two of those three without having to subscribe to them all. There's no reason I can see to force an artificial DiscontinuitySpike in order to match somebody's category system.

Sure, you can use one or two of polymorphism, inheritance or encapsulation, but then it's not OO.

I have to disagree with your view of what "OOP" is. It doesn't matter anyhow here, for one should design software based on the best design choices, NOT based on vocabulary. You can't make something more efficient or more parsimonious or more economical by redefining it. (Caveat: changing the definition of the goals/metrics or "economics" may affect such.)

I see no logic of the universe that forces a hard distinction between maps and OOP as far as how to use them, even IF I buy your definition. It's not in for a penny, in for a pound. Even if I buy your def, there is a continuum between a map and a "true" object and no clear reason to ignore the continuum or pretend like it doesn't exist or pretend like if we are 70% fitting "true OO" we should go 100% because 70% is "bad" or 30% is "bad" and 100% is "good".

If maps are sufficient for OO, then do the static maps in C (i.e., the 'struct' construct) mean C is object oriented?

That depends on how one defines "OO"[1]. The definition is not really what matters and I don't want to get caught up in another term fight. The point is that useful code constructs can exist that cover the full gamut between and including a pure map ("is" or "used as") and a fully encapsulated object (no public "variables", only methods). What we call these things is irrelevant and shouldn't dictate how we lay out our code. It's silly to say that as soon as one introduces a single method into a map (or object used like a map), then one is suddenly obligated to wrap every key of the map or map-like thing. If I understand your argument correctly, then this all-or-nothing rule would apply under it. I find it a ludicrous and highly artificial "boundary". -t

By the way, if full encapsulation is always the "proper" way to do OO, then an "OOP language" technically shouldn't allow public variables in classes at all: only methods would be able to read and change class variables.

Damn straight. OOP languages shouldn't allow public variables in classes at all. Ever. I don't know why they do allow it.

Because unless the language is carefully-designed to avoid such, it creates bloat, and bloat slows down reading and creates errors due to bloat-induced reading mistakes. They don't do it because they probably don't want the bloat-related problems associated with it.

By the same argument, structured programming is bloat compared to the simplicity of GOTOs, and slows down reading and creates errors due to bloat-inducing reading mistakes. Typical OO languages don't force member variables to be private purely for historical reasons. Modern OO practice does not make member variables public.


[1] In my book, it would only be OO if it facilitates defining and putting behavior (functions or references to functions) in the 'struct' nodes. And OO-ness can perhaps be considered on a continuum rather than discrete (is or isn't OO). If C makes it possible but difficult to put and use behavior in struct nodes, then it may be considered "weak OO". "Orientation" generally means "leaning toward". Thus, "object oriented" generally means "leaning toward objects". This can be read as "facilitates object-ish things and usage" or "makes it easier to do object-ish stuff". -t

So you define OO by "has dotted dispatch?"

I don't recognize the phrase "dotted dispatch".

E.g.:
 instance.method()
Note the dot between the instance and the method name. That's dotted dispatch.

Note also that 'class p {void x()};' is merely a syntactic shorthand for 'class c {}; void x(c p);' In terms of TypeSafety, etc., they are equivalent, but the latter requires that 'class p' have publicly-accessible members whilst in the former, method 'x' can access private members of 'class p'. In short, the reason for "putting behavior ... in the 'struct' nodes" instead of outside the 'struct' nodes is specifically to AvoidDirectAccessOfMembers.


CategoryJava, CategoryInterface, CategoryObjectOrientation
NovemberFourteen

EditText of this page (last edited November 27, 2014) or FindPage with title or text search