C++ Coding Standards : 101 Rules, Guidelines, and Best Practices

K

kanze

Phlip said:
ma740988 wrote:
OO design is about behavior. Classes should tell others what to do. If
a class takes data out of another, manipulates it, and puts it back
in, then the behavior is in the wrong class.
If you indeed share data, then this "data transfer object" should be a
separate thing, without any behavior, and the classes should pass it
around.

Which begs the question: what should you do about these shared objects?

My experience suggests that there are two radically different types of
"data classes". The first are your classical value classes, which
typically, you copy (although if the value has a lot of state, e.g. a
matrix of some sort, you might prefer to avoid copying for performance
reasons). This includes all of the basic types -- a double has a state,
and there is an allowed set of operations on it, but any identity that
it has is fully represented by its value. The second are entity data
sets (or whatever you want to call them -- I've yet to find a good
name). Typically, they are collections of data which have an identity;
if they are copiable or assignable, it is to support transactions and
roll-back, and that is all. Often, I find it useful to support two
interfaces to such classes, one read-only, and a second which allows
mutation. Generally, such classes are maintained by some sort of
dedicated collection or manager. Such classes are, however, usually
pure data; true behavior and business logic are situated elsewhere (and
generally depend on more than one object of this sort).

IMHO, depending on the applications, setters and getters may be
perfectly appropriate for this second category. In other applications,
just using a struct may be even more appropriate.

In the end, I think the warning is appropriate. If you find that most
of your classes, regardless of their role in the system, are full of
getters and setters, you are probably doing something wrong. On the
other hand, if the role of the class is a data holder, then you have
encountered the obvious exception which confirms the rule. Of the many,
many different roles a class can have, there is only one (or very few)
where the exception is really justified. The fact that there are
classes in this role in a lot of applications, and that in some
particular applications, they may even represent the majority of
classes, is irrelevant. It's still just one role.
 
J

Jerry Coffin

[ ... ]
There is no fallacy in appeal to authority when the subject is one of widely
help opinions in a field. The opinions of authorities in the field
constitute the topic.

In the case of what a phrase is generally understood to mean, I'd have
to agree. In this case, however, the people you quote don't seem to be
the most widely recognized people in the field; worse, you seem to be
(at best) warping what they're saying, to at lesat some degree, to get
what you want out of it rather than reading it for what it really
says.

Most of the better-recognized "experts" do seem to agree that "object
oriented" is only really correct when inheritance is used, not simply
when objects are used.

Just for a few examples: _Object Oriented Analysis and Design With
Applications_ (Grady Booch) defines object oriented programming as:

Object-Oriented Programming is a method of implementation in which
programs are organized as cooperative collections of objects, each
of which represent an instance of some class, and whose classes
are
all members of a hierarchy of clases united via inheritance
relationships.

He then dissects this, pointing out three salient points: 1) objects,
2) classes, 3) inheritance. He then reiterates the requirement for
inheritance, saying "Specifically, programming without inheritance is
distinctly not object oriented: we call it programming with abstract
data types."

He also quote Cardelli and Wegner's definition of an object-oriented
langauge, which (unsurprisingly) also has three elements, closely
matching those for object-oriented programming above.
The fundamental concept of object oriented programming is the idea of data
objects combined with associated operators.

This concept is necessary, but insufficient by almost every definition
extant.

[ ... ]
This seems like a far weaker statement than the idea that using set and get
methods indicated a probable design flaw. That was, IIRC, the point at
which this thread forked. Stroustrup makes a similar assertion in
TC++PL(SE) regarding set and get methods. I have questioned that in this
newsgroup, and I have never been convinced of the merits of the general
guideline. Perhaps a better statement might be 'Be judicious in what you
expose through accessor methods'. That I can agree with.

I'd put it a bit more strongly than that -- I'd consider an accessor
roughly on a par with a goto. Sometimes it's bad and other times it's
mearly mediocre -- but in the end, _nearly_ the only good excuse for
using either one is that what you're working on is simplky a quick
hack, and doing something better simply isn't justified.

I know, there are people who will claim that they have a piece of code
that justifies using a goto, and others who show what they claim is
justification for an accessor method. Unfortunately nearly every
example of "good" uses of accessors starts out with a poor
specification (on the order of: "this class needs to provide access to
X...and therefore it needs to include an accessor"). In nearly every
case, it also turns out to be fairly incomplete, with little or no
thought about how the rest of the system would/will really work.

OTOH there almost certainly ARE some kinds of classes that need
accessors, or at least things that try to look like them. An obvious
example would be something like std::vector or std::map, that simply
stores data and allows the user to retrieve it when needed. In this
case, 'store' and 'retrieve' data, translates almost directly to
things like look like accessors, although (especially in the case of
map) they will typically be implemented as fairly complex algorithms.

Classes like these, however, rarely provide particularly high levels
of abstraction. They're a step up from working with raw memory and
such (e.g. C arrays) but they (usually) still don't relate very
closely to solving the problem at hand. As such, while their existence
and use is usually easy to justify, you should normally strive to bury
them inside of classes that relate more closely to the real-world
problem being solved -- i.e. code that operates at a higher level of
abstraction, further away from simply storing and retrieving data in
memory.
 
S

Steven T. Hatton

Jerry said:
[ ... ]
There is no fallacy in appeal to authority when the subject is one of
widely
help opinions in a field. The opinions of authorities in the field
constitute the topic.

In the case of what a phrase is generally understood to mean, I'd have
to agree. In this case, however, the people you quote don't seem to be
the most widely recognized people in the field; worse, you seem to be
(at best) warping what they're saying, to at lesat some degree, to get
what you want out of it rather than reading it for what it really
says.

Are you suggesting Dahl is not regarded as one of the authorities in OOP?
That's the only other person I recall quoting in this thread.

I'd put it a bit more strongly than that -- I'd consider an accessor
roughly on a par with a goto. Sometimes it's bad and other times it's
mearly mediocre -- but in the end, _nearly_ the only good excuse for
using either one is that what you're working on is simplky a quick
hack, and doing something better simply isn't justified.

So accessing the contents of a std::vector<> using an index is bad design?


--
"If our hypothesis is about anything and not about some one or more
particular things, then our deductions constitute mathematics. Thus
mathematics may be defined as the subject in which we never know what we
are talking about, nor whether what we are saying is true." - Bertrand
Russell
 
S

Stuart Gerchick

Frank Birbacher said:
Hi!



There are also European people on this newsgroup, like me. And
they don't have the book yet.

Frank

I am based in the UK myself these days, which is why I dont have the book yet
 
M

Maxim Yegorushkin

ma740988 said:
A host of get and sets - I suspect - are signs of poor design. I'd
still like to see a concrete example that shows the solution.

Once I worked on a project that involved object/relational mapping. It
included Data Access layer, comprised of data objects and a data mapper.
Data objects were mere state without any behavior with typed
getters/setters and were used as data primitives for a higher layer -
Object Domain Model. Data mapper was responsible for mapping data objects
to and from relational model (sql database).

To reduce the efforts while implementing the data mapper all data objects
contained generic state:

typedef std::string field_name;
typedef std::map<field_name, variant> state;

So, the data mapper knew nothing about data object classes and could only
deal with raw generic state (actually it could do a lot more things, but
I'll spare you).

Data objects were interfaces with bunches of getters/setters only that
provided strong typing to the generic state, represented like:

struct InstrumentData : DatabaseObject
{
typedef InstrumentData SuperClass;

virtual tstringp Name() = 0;
virtual tstringp FullName() = 0;
virtual tstringp IsinCode()= 0;
virtual tstringp CfiCode()= 0;
virtual uint32p DaysToSpot() = 0;
virtual UUIDp PricingCurrencyId() = 0;
virtual CurrencyDataP PricingCurrency() = 0;
};

Those types ending with small p (style matters aside) were proxy objects
like:

template<class T>
struct value_proxy
{
value_proxy(varant& v) : v_(&v) {}
operator T const&() const { return varant_cast<T&>(*v_); }
value_proxy const& operator=(T const& t) { varant_cast<T&>(*v_) =
t }
value_proxy const& operator=(value_proxy const& p)
{ varant_cast<T&>(*v_) = static_cast<T const&>(p); }
variant* v_;
};

typedef value_proxy<uint32> uint32p;

So, putting all together, implementation of those data objects were like:

struct InstrumentDataImpl : InstrumentData
{
state state_;
// ...
uint32p DaysToSpot() { return state_["days_to_spot"]; }
// ...
};

And a user worked with data objects like:

smart_ptr<InstrumentData> id;
// ...
uint32 day = id->DaysToSpot();
id->DaysToSpot() = day + 1;

So, to me it looked like a good and usefull application of getters/setters
(funny, but it also was the only one I could think of).
 
M

Michiel Salters

Steven T. Hatton said:
Phlip wrote:



I don't know how widely shared that opinion is. One view of OOP is
expressed in Roman Mäder's _Computer Science With Mathematica_ as follows:
"An object is, therefore, a collection of data elements together with the
functions operating on them."

That is exactly the same. The example is not OO, because the collection
of data elements is in one class and the functions operating on them are
in another. Maeder states that these should not be distributed over two
objects, instead they should be in the same object.

Regards,
Michiel Salters
 
P

Phlip

Michiel said:
Steven T. Hatton wrote:

That is exactly the same. The example is not OO, because the collection
of data elements is in one class and the functions operating on them are
in another. Maeder states that these should not be distributed over two
objects, instead they should be in the same object.

"How" to partition behavior into methods is an aspect of design, in general.
If a given design happened to partition the data here and their functions
there, it wouldn't be "less OO".

Put another way, I can't say my design is better than yours because it's
"more OO", or because it gloms all functions and all their data into the
same objects. Such a claim brings nothing to the debate whether my design is
any good. (A more useful claim would be my design is easier to safely
upgrade.)

OO is when a language permits partitioning along a virtual dispatch system
typesafely synchronized with an object's encapsulation interface.

Targeting the Subject line, all designs should reduce their primitive data's
exposure to getting and setting, whether they are OO or not.
 
S

Steven T. Hatton

ma740988 wrote:

Here's one that I'm still confused - if you will - on what constitutes
the ideal implementation approach.

42 per the text. Dont give away your internals

Accessor and mutator as I understand it are - for the most part -
design flaws.
The example in the text shows a GetBuffer member function returing a
char*
In any event, when data needs to be shared among classes this
accessor/mutator beats the alternative (public member data) so I've
never quite understood this one.
A host of get and sets - I suspect - are signs of poor design. I'd
still like to see a concrete example that shows the solution.

My 2cents for what it's worth.

OK, I bought the book, and can now see what it says. It really isn't
talking about accessor/mutator functions, per se. It's saying not to
expose internal state in such a way as to compromise the class's
ability to
maintain a consistent state(invariant). Furthermore, don't expose
internals which are subject to change without notification to the user.

Don't give unaccountable strangers your credit card number.
Don't write checks for which you cannot guarantee the availability of
funds
for a reasonable duration.
If you do write a check that can only be guaranteed for a short period,
stipulate the condition.
Don't build a massive conventional force and fail to secure your
borders
against unconventional combatants.

--
"If our hypothesis is about anything and not about some one or more
particular things, then our deductions constitute mathematics. Thus
mathematics may be defined as the subject in which we never know what
we
are talking about, nor whether what we are saying is true." - Bertrand
Russell
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,766
Messages
2,569,569
Members
45,042
Latest member
icassiem

Latest Threads

Top