S
Steven T. Hatton
Stroustrup's view on classes, for the most part, seems to be centered around
the notion of invariants. After a bit of adjusting to the use of yamt (yet
another math term) in computer science, I came to appreciate some of the
significance in what he is asserting. I believe a good example would be
the Riemann-Christoffel curvature tensor. In the 4-space of general
relativity, there are 256 components of this beast.
One approach to representing this tensor might be a struct with 256 float
members all public. The user simply treats it as a big bag of bits. This
tensor - by definition - has certain invariant properties which must remain
constant under certain groups of operations. That means that, either the
user must be carful to enforce the rules preserving the invariants if he is
going to modify the components directly. For this reason, it makes sense
to put the data in a class and only allow controlled access through the use
of invariant preserving access methods.
Stroustrup also argues that a reasonable test to determine if you really do
have a class with invariants is to ask if there are multiple
representations of the data structure which are functionally equivalent.
Indeed there are for this tensor. And that can actually buy you a lot.
Although the tensor has 256 components, only 20 are independent. That
means you can represent the tensor as an object of only 20 data members,
and if circumstances demand all 256 components be available in the program,
that can be accomplished by multiply indexing the storage locations through
access methods which collectively give the illusion that the entire tensor
exists with all of it's 256 components.
So far, so good. I pretty much agree with his reasoning for distinguishing
between what should properly be represented as a class with private (or
protected) data, as opposed to simply a struct with all public members and
direct user access. I like a lot of the careful discernment found in C++ as
opposed to Java, for example. There are not concepts of constness and
mutable caching in typical Java literature. That shows me C++ has
expressive powers that go well beyond other general purpose languages.
There is however, one point that Stroustrup doesn't address regarding
'accessor' methods. He tells us we should avoid writhing classes with
direct accessor methods to manipulate the data. The reasoning seems
obvious to me. If the user has a reason to micro-manage the data, it
probably doesn't have a clearly defined invariant that could and should be
preserved under the operations applied to it collectively.
The Java community operates with a different philosophy regarding data and
classes. Basically, nothing is public other than static constants, and
accessor methods. The simplest notion of a been in Java is just a class
with data members read and written through the mediation of 'set' and 'get'
methods. The payoff to this very protective approach to managing data is
that it facilitates such design features as event-listener patterns, and
concurrent programming. The access methods provide a natural means of
locking access to data, and also monitoring access for purposes of event
notification.
One of the more important developments in TC++PL(SE) is the creation of
abstract user interface components as a means of demonstrating the various
options for using inheritance and class hierarchies. Stroustrup argues -
correctly IMO - that a programmer should strive to solve problems
independently of the proprietary libraries he may be developing with.
This is what I have been calling an AUI (abstract user interface) for a few
years. I decided I would try the exercise as part of a project I'm working
on with the goal of visually representing the various access patterens use
to simulate multidimensional arrays using valarray, slices, and gslices.
The program creates a tree of grid cell objects which become the elements
of the graphical representation of the multidimensional matrix.
I created an interface class with all pure virtual functions, and one data
member. The data member is an object that holds default values to be
shared among the different grid elements. This may seem somewhat
convoluted, but it enables me to do some interesting things, such as change
the value of the default values object, notify all the grid cell objects
which own a DefaultBox that their defaults have been changed, and they need
to consider updating.
That demonstrates what I was getting at regarding the use of an
event/listener (observer) pattern to adapt to changes in a particular
object. There's something else that my design also seems to dictate.
The class used to default initialize grid cells is a good example of what
I'm finding appropriate for my design, and which seems to contradict
Stroustrup's recommendation to avoid 'set' and 'get' functions. That's
actually a pretty significant part of what my grid cell objects consist of.
That is, data members with read and mutate functions for the data.
#ifndef STHBOXDEFAULTS_H
#define STHBOXDEFAULTS_H
#include <string>
namespace sth
{
using std::string;
/**
@author Steven T. Hatton
*/
class BoxDefaults
{
public:
BoxDefaults( bool is_leaf_ = false,
bool is_vertical_ = false,
string text_ = "[...]",
RgbColor bg_color_ = RgbColor::YELLOW,
RgbColor text_color_ = RgbColor::BLACK,
RgbColor edge_color_ = RgbColor::RED,
double h_ = 35,
double w_ = 50,
double border_w_ = 5,
double x_ = 0,
double y_ = 0,
int bg_z_ = 1,
int text_z_ = 2
)
: is_leaf( is_leaf_ ),
is_vertical( is_vertical_ ),
text( text_ ),
bg_color( bg_color_ ),
text_color( text_color_ ),
edge_color( edge_color_ ),
h( h_ ),
w( w_ ),
border_w( border_w )
{}
virtual ~BoxDefaults(){}
/**
* Are child boxes layed out vertically?
*/
virtual bool Is_vertical() const
{
return this->is_vertical;
}
virtual void Is_vertical(const bool& is_vertical_)
{
this->is_vertical = is_vertical_;
}
/**
* Is this a leaf node?
*/
virtual bool Is_leaf() const
{
return this->is_leaf;
}
virtual void Is_leaf(const bool& is_leaf_)
{
this->is_leaf = is_leaf_;
}
virtual RgbColor Bg_color() const
{
return this->bg_color;
}
virtual void Bg_color(const RgbColor& bg_color_)
{
this->bg_color = bg_color_;
}
/* etc., etc. */
};
}
This seems rather natural to me, but it makes me a bit uneasy, because
Stroustrup has suggested it may not be a good design approach. I will not
that he also insists that his advice and opinions are not intended as
inviolable dictates, nor are they expected to be applicable equally in all
circumstances.
What is your opinion of the use of the 'Java Beans' design approach? I
realize there is more to a Bean than simply a class with data, events, and
associated listeners. Nonetheless, that seems to be the definitive essence
of what they areally are. There are some aspects of the JavaBean
specification that strike me as arcane and seem to offer far more potential
trouble than potential worth. I do believe the general approach is
something worth understanding, and considering for C++ design. Here's the
JavaBean spec:
http://java.sun.com/products/javabeans/docs/spec.html
What do you think of these ideas as potentially applicable to C++ code?
the notion of invariants. After a bit of adjusting to the use of yamt (yet
another math term) in computer science, I came to appreciate some of the
significance in what he is asserting. I believe a good example would be
the Riemann-Christoffel curvature tensor. In the 4-space of general
relativity, there are 256 components of this beast.
One approach to representing this tensor might be a struct with 256 float
members all public. The user simply treats it as a big bag of bits. This
tensor - by definition - has certain invariant properties which must remain
constant under certain groups of operations. That means that, either the
user must be carful to enforce the rules preserving the invariants if he is
going to modify the components directly. For this reason, it makes sense
to put the data in a class and only allow controlled access through the use
of invariant preserving access methods.
Stroustrup also argues that a reasonable test to determine if you really do
have a class with invariants is to ask if there are multiple
representations of the data structure which are functionally equivalent.
Indeed there are for this tensor. And that can actually buy you a lot.
Although the tensor has 256 components, only 20 are independent. That
means you can represent the tensor as an object of only 20 data members,
and if circumstances demand all 256 components be available in the program,
that can be accomplished by multiply indexing the storage locations through
access methods which collectively give the illusion that the entire tensor
exists with all of it's 256 components.
So far, so good. I pretty much agree with his reasoning for distinguishing
between what should properly be represented as a class with private (or
protected) data, as opposed to simply a struct with all public members and
direct user access. I like a lot of the careful discernment found in C++ as
opposed to Java, for example. There are not concepts of constness and
mutable caching in typical Java literature. That shows me C++ has
expressive powers that go well beyond other general purpose languages.
There is however, one point that Stroustrup doesn't address regarding
'accessor' methods. He tells us we should avoid writhing classes with
direct accessor methods to manipulate the data. The reasoning seems
obvious to me. If the user has a reason to micro-manage the data, it
probably doesn't have a clearly defined invariant that could and should be
preserved under the operations applied to it collectively.
The Java community operates with a different philosophy regarding data and
classes. Basically, nothing is public other than static constants, and
accessor methods. The simplest notion of a been in Java is just a class
with data members read and written through the mediation of 'set' and 'get'
methods. The payoff to this very protective approach to managing data is
that it facilitates such design features as event-listener patterns, and
concurrent programming. The access methods provide a natural means of
locking access to data, and also monitoring access for purposes of event
notification.
One of the more important developments in TC++PL(SE) is the creation of
abstract user interface components as a means of demonstrating the various
options for using inheritance and class hierarchies. Stroustrup argues -
correctly IMO - that a programmer should strive to solve problems
independently of the proprietary libraries he may be developing with.
This is what I have been calling an AUI (abstract user interface) for a few
years. I decided I would try the exercise as part of a project I'm working
on with the goal of visually representing the various access patterens use
to simulate multidimensional arrays using valarray, slices, and gslices.
The program creates a tree of grid cell objects which become the elements
of the graphical representation of the multidimensional matrix.
I created an interface class with all pure virtual functions, and one data
member. The data member is an object that holds default values to be
shared among the different grid elements. This may seem somewhat
convoluted, but it enables me to do some interesting things, such as change
the value of the default values object, notify all the grid cell objects
which own a DefaultBox that their defaults have been changed, and they need
to consider updating.
That demonstrates what I was getting at regarding the use of an
event/listener (observer) pattern to adapt to changes in a particular
object. There's something else that my design also seems to dictate.
The class used to default initialize grid cells is a good example of what
I'm finding appropriate for my design, and which seems to contradict
Stroustrup's recommendation to avoid 'set' and 'get' functions. That's
actually a pretty significant part of what my grid cell objects consist of.
That is, data members with read and mutate functions for the data.
#ifndef STHBOXDEFAULTS_H
#define STHBOXDEFAULTS_H
#include <string>
namespace sth
{
using std::string;
/**
@author Steven T. Hatton
*/
class BoxDefaults
{
public:
BoxDefaults( bool is_leaf_ = false,
bool is_vertical_ = false,
string text_ = "[...]",
RgbColor bg_color_ = RgbColor::YELLOW,
RgbColor text_color_ = RgbColor::BLACK,
RgbColor edge_color_ = RgbColor::RED,
double h_ = 35,
double w_ = 50,
double border_w_ = 5,
double x_ = 0,
double y_ = 0,
int bg_z_ = 1,
int text_z_ = 2
)
: is_leaf( is_leaf_ ),
is_vertical( is_vertical_ ),
text( text_ ),
bg_color( bg_color_ ),
text_color( text_color_ ),
edge_color( edge_color_ ),
h( h_ ),
w( w_ ),
border_w( border_w )
{}
virtual ~BoxDefaults(){}
/**
* Are child boxes layed out vertically?
*/
virtual bool Is_vertical() const
{
return this->is_vertical;
}
virtual void Is_vertical(const bool& is_vertical_)
{
this->is_vertical = is_vertical_;
}
/**
* Is this a leaf node?
*/
virtual bool Is_leaf() const
{
return this->is_leaf;
}
virtual void Is_leaf(const bool& is_leaf_)
{
this->is_leaf = is_leaf_;
}
virtual RgbColor Bg_color() const
{
return this->bg_color;
}
virtual void Bg_color(const RgbColor& bg_color_)
{
this->bg_color = bg_color_;
}
/* etc., etc. */
};
}
This seems rather natural to me, but it makes me a bit uneasy, because
Stroustrup has suggested it may not be a good design approach. I will not
that he also insists that his advice and opinions are not intended as
inviolable dictates, nor are they expected to be applicable equally in all
circumstances.
What is your opinion of the use of the 'Java Beans' design approach? I
realize there is more to a Bean than simply a class with data, events, and
associated listeners. Nonetheless, that seems to be the definitive essence
of what they areally are. There are some aspects of the JavaBean
specification that strike me as arcane and seem to offer far more potential
trouble than potential worth. I do believe the general approach is
something worth understanding, and considering for C++ design. Here's the
JavaBean spec:
http://java.sun.com/products/javabeans/docs/spec.html
What do you think of these ideas as potentially applicable to C++ code?