Sign in to follow this  
Jiia

Variable Exposure - Epidemic Alert

Recommended Posts

I've been coding in C++ for 4 or 5 years now, and I have shamelessly used public member variables in every class I've ever designed. I'm trying to change my horrible habits.
Quote:
Never use public or protected member data.
This is something I ran into, within a list of programming guidelines for OO decency. Why not have member data as protected? Do classes publically derived from base classes inherit the protected data? What about the private data? I know I sound like an idiot, but I've never attempted to understand the issues around protected and private declaration, or the difference between them. Are there any good tutorials or documents out there specifically written to help educated but corrupted programmers? :) Here's a scenario for ya. Say I have a class that is written correctly (term for non-exposure?). If I include that class as a member variable in another class, can I expose that data? It's own data is hidden from access, so why hide the actual class member? Let me know if you don't follow what I'm describing. Here's another. What if I have a GUI system which doesn't have it's own data, but uses pointers to the actual runtime data to do its work? How can I respectably give a generic GUI control total acces to modify a data member (generic variable, such as int, float, char, etc) within a class which has that data as protected / private? Supplying a pointer to it would completely destory the whole point, right? I have other questions, but I don't want to be a leech :) The more simple your terms of are, the higher I'll rate ya ;) Thanks for any advice.

Share this post


Link to post
Share on other sites
First of all, whenever a programming text says "Never do XXXX", it usually means "XXXX is generally considered a BAD thing for a variety of reasons, but is useful in some circumstances". Remember these are guidelines you're reading, not hard and fast rules that you must follow.

Anyway, in most practical cases, the superclass will either be an interface class - in which case it doesn't require any data at all - or a fully self contained class in it's own right - in which case the subclasses can access/manipulate the data in the same way that everything else does, through the use of suitable member functions.

In your first scenario, I suppose you could expose it directly, but it's still not recommended, since the parent class may need to perform initialization, or other operations on it. Strictly speaking it would be better to use inlined accessors or else rethink your design.

I'm not sure I fully understand your second scenario, but again it sounds like something which could be resolved with a rethink of your implementation.

Share this post


Link to post
Share on other sites
Quote:
Original post by Jiia
Why not have member data as protected? Do classes publically derived from base classes inherit the protected data? What about the private data?


I think it would be best to describe why we encapsulate a type's representation from other unrelated types/modules using private access in the first place before talking about why it's best practice not to use protected data members.

The two main reasons why we encapsulate a type's representation is to:

1. Provide loose coupling among modules, meaning that if a library of modules/types relies on another module/type's representation and we decide that the representation for it is incorrect/unsuitable and we make changes in that module/type then every other module/type needs to be updated. Where as if they all relied on the interface of the module/type instead it's easy to change the repsentation without effecting other modules/types.

2. To maintain the state invariant, meaning to avoid putting an instance of some type in an underfined state. If the data members are only allowed certain values in a certain ranges and you allowed direct access how could you stop users of that instance from putting invalid/out-of-range values?

Now we know the main reasons why we encapsulate a type's representation from other unrelated types/modules we'll talk about protected members.

If you make a data member protected your basically contradicting your self, why? well you've encapsulated from unrelated types/modules but you allow clients direct access from sub-types and now instead of avoiding tight coupling among unrelated types you've just made tight coupling with related types and the possiablity of invalidating the state invariant making an instance of the sub-type in a undefined state.

Probably see where this is going, a type's representation is just minor details, it's interface is more important, this is especially true for abstract data types (ADT) where they can have more than one representation for example the ADT Stack there are probably infinite number of ways to represent the internals of a stack but no one cares we just care what we can do to stacks, it's behaviour/operations/interface.

Quote:
Original post by Jiia
Here's a scenario for ya. Say I have a class that is written correctly (term for non-exposure?). If I include that class as a member variable in another class, can I expose that data? It's own data is hidden from access, so why hide the actual class member? Let me know if you don't follow what I'm describing.


I don't follow please elaborate more clearly.

Quote:
Original post by Jiia
Here's another. What if I have a GUI system which doesn't have it's own data, but uses pointers to the actual runtime data to do its work? How can I respectably give a generic GUI control total acces to modify a data member (generic variable, such as int, float, char, etc) within a class which has that data as protected / private? Supplying a pointer to it would completely destory the whole point, right?


Well first you should prefer returning constant references over pointers but anyways provide a constant member function that returns constant pointer or constant reference.

[Edited by - snk_kid on August 3, 2004 11:45:43 AM]

Share this post


Link to post
Share on other sites
You make data private for a reason - its status is important and you need to control access to it so that it doesn't become invalid. Anyone outside of the class shouldn't have to worry about the object being in an invalid state.

These reasons still apply to protected data. You're saying 'only derived classes should have access to this data - anyone else might modify it in an inconsistent way'. But if there is that danger, derived classes are just as capable of messing up the state of the object. So it's a bit of a half baked idea.

Bjarne Stroustrup (the language's inventor) had this to say about it:
Quote:
I think that overstates the case against `protected' a bit. here is the actual quote:

Five years or so later, Mark banned the use of protected data
members in Interviews because they had become a source of bugs:
``novice users poking where they shouldn't in ways that they ought
to have known better than.'' They also seriously complicate
maintenance: ``now it would be nice to change this, do you think
someone out there might have used it?'' Barbara Liskov's OOPSLA
keynote gives a detailed explanation of the theoretical and
practical problems with access control based on the `protected'
notion. In my experience, there have always been alternatives
to placing significant amounts of information in a common base
class for derived classes to use directly. In fact, one of my
concerns about `protected' is exactly that it makes it too easy
to use a common base the way one might sloppily have used global
data.

Fortunately, you don't have to use protected data in C++;
`private' is the default in classes and is usually the better
choice. Note that none of these objections are significant for
protected member functions. I still consider `protected'
a fine way of specifying operations for use in derived classes.


The Design and Evolution of C++, sec13.9.

However, I do consider reliance on protected data a dubious practice in any language.


I was recently reading Large Scale C++ Design by Lakos and was interested by his practice.

Basically, the main use of protected is for member functions and it allows you to call them in a controlled manner. This is useful (primarily in my opinion) for the Template Method Pattern. But Lakos considers it to be putting implementation details in the public interface of the class. If I'm a user of the class I don't care what its protected functions are. I'm not allowed to call them.

So Lakos suggests you could move those functions and put them in an implementation class (this is something he does all the way through and it is now well known as the PimplIdiom). But this is a specific application of it to remove protected members from the public interface. So the protected functions go into another class, but you make them public. Then people derive from that class instead of the original one.

In terms of good design, on the whole, making your interface classes have data protected or even private isn't enough. You need to remove the details completely.


//SomeClass.h
class SomeClass {
private:
std::string m_name;
public:
void SetName(const char* name) {
m_name = name;
OnSetName(name);
}
protected:
virtual void OnSetName(const std::string& name) = 0;
};

becomes

//SomeClass.h
class SomeClassImpl;//will be the private functionality
class SomeClassStrategy;//will be the protected functionality
class SomeClass {
private:
SomeClassImpl* m_pImpl;
public:
void SetName(const char* name);
void SetStrategy(const SomeClassStrategy*);
};

You implement SomeClassImpl elsewhere. Users can derive from SomeClassStrategy. All you have in the public interface is the public members. You have decoupled the implementation details and can change them without users of having to recompile.

Hope that all makes sense. I've rambled a bit, but basically, you can remove protected and private data and functions from your class interface. The data becomes private and the functions become public. The implementation is never made available to users.

This is useful and it is good to understand the benefits. These are rules to do with good large scale design. It isn't morally wrong but could be a design problem. Never say never though.

Share this post


Link to post
Share on other sites
You know, at the heart of this question is the question "Who are we designing our classes for?"

Often the answer to this is ourselves and therefore the whole data hiding thing seems silly because we can't hide it from ourself because we know it's there and we know the bounds and limits and uses of that data, so when we our the class writer and user it seems to add a lot of unneeded redirection.

So I guess the bottom line to ask yourself when you are writing a class is who will be the end user? If you are sure it's only for yourself then I wouldn't worry about hiding and continue using public members.

On a related note though, hiding from yourself may be benificial too if you ever think you might have to come back to a complicated class you havn't worked with in a long time. Though, since you've got the source, you can always go back in and review/relearn it.

D

Share this post


Link to post
Share on other sites
Quote:
Original post by darrylsh
You know, at the heart of this question is the question "Who are we designing our classes for?"

Often the answer to this is ourselves and therefore the whole data hiding thing seems silly because we can't hide it from ourself because we know it's there and we know the bounds and limits and uses of that data, so when we our the class writer and user it seems to add a lot of uneeded redirection.

So I guess the bottom line to ask yourself when you are writing a class is who will be the end user? If you are sure it's only for yourself then I wouldn't worry about hiding and continue using public/private.

On a related note though, hiding from yourself may be benificial too if you ever think you might have to come back to a complicated class you havn't worked with in a long time. Though, since you've got the source, you can always go back in and review/relearn it.

D


And the question is why are we creating a user-defined types for are selfs? The common answer is to build a portfolio of are skills to show it to employers to get a job so do we really wanna show sloppy code that no-one can decipher/decrypt?

I'm not saying that these good principles apply for every-case, like a 2d vector/point is valid to have it's members public because there is no limit/range on the values the members can have, the representation is never gonna change.

Share this post


Link to post
Share on other sites
Quote:
Original post by darrylsh
You know, at the heart of this question is the question "Who are we designing our classes for?"


The point of encapsulation is not so much to hide implementation details from another programmer, but to ensure that classes are self contained and loosely bound to one another, resulting in modular, reusable code which can be expanded without necessarily breaking the code that depends on it.

Of course, properly encapsulated reusable code is not something you'll learn to write overnight, and there may be projects for which it simply isn't worth the extra effort. It's very easy to get caught in the trap of overengineering everything, with the result that instead of getting unreusable but functional code, you end up with no working code at all.

Share this post


Link to post
Share on other sites
Quote:
Original post by darrylsh
You know, at the heart of this question is the question "Who are we designing our classes for?"

Often the answer to this is ourselves and therefore the whole data hiding thing seems silly because we can't hide it from ourself because we know it's there and we know the bounds and limits and uses of that data, so when we our the class writer and user it seems to add a lot of unneeded redirection.


Taken to an extreme you could say you'll have all data public in your classes/structures and no member functions. You could make all your data global too.

But your design would be unmanageable.

The reason you write code in a professional manner isn't because someone is looking over your shoulder, or even because some prospective employer may someday see it. You write good code because it's good code.

Share this post


Link to post
Share on other sites
I really appreciate all of the help and advice. I've learned more in the last few minutes than in the last few months. Kind of sad, really.

Quote:
Original post by petewood
These reasons still apply to protected data. You're saying 'only derived classes should have access to this data - anyone else might modify it in an inconsistent way'. But if there is that danger, derived classes are just as capable of messing up the state of the object. So it's a bit of a half baked idea.

Ahh, I very much like the simplified example. This was very helpful. So all derived classes still inherit all member data variables, no matter which section they are listed under? But the point is that in private, the derived class cannot modify it's base classes' data, just as the outside world cannot modify it? But protected is still very useful to include tasks for that class to run on itself. This really does make a lot of sense.

My current game has 1.46 megs of poorly written source code, so I'm gonna be busy for quite a while.

I don't seem to use pure interface classes very often. My interface classes usually contain data which is needed for all derived objects. Such as CObject, which has a position vector, which is needed by CCharacter and CItem. That's a simple example; the number of actual variables is pretty high. My code is written now in such a way that CObject has member functions which handle almost everything that both characters and items need to have handled. There are very few functions which are overridden. Most are virtual, and the character and item classes use many CObject member functions to perform their tasks.

There are some situations where not having data as public or protected would seem to be a real pain. My CObject class has a CBitflag variable. CBitflag is a correctly encapsulated class that has member functions to handle tasks with it's own bitflag variable. How do I allow characters to turn on and off state-flags without having to write the CBitflag class interface into the CObject class interface? Example:

class CBitFlags
{
public:
CBitFlags() { Clear(); }

VOID Clear() { Data = 0; }
VOID Fill() { Data = 0xFFFFFFFF; }

BOOL Bits(ULONG FlagValue) { return Data & FlagValue; }
VOID TurnOn(ULONG FlagValue) { Data |= FlagValue; }
VOID TurnOff(ULONG FlagValue) { Data &= ~FlagValue; }
VOID Toggle(ULONG FlagValue) { Data ^= FlagValue; }

ULONG GetData() { return Data; }

operator = ( CONST ULONG FlagValues ) { Data = FlagValues; }

private:

ULONG Data; // Flag Data
};


class CObject
{
public:
...
private:

CBitFlags FStatus;
...
};

class CCharacter : public CObject
{
public:
VOID TakeHealth(INT value)
{
HurtMe(value);
if(GetHealth() < 0)
FStatus.TurnOn(STATUS_DEAD);
}
};



There are many flags defined for FStatus. Well... 32 :) And this is just an example; I know I could allow CObject to handle deaths, but this is not the case with all flags. And I cannot access them from CCharacter if FStatus is private. What can I do?

I almost forgot to ask one other question. Why derive classes as public? As in CCharacter : public CObject. What happens when you derive them private or protected? Again, sorry if this is a dumb question.

Thanks again.

Share this post


Link to post
Share on other sites
Quote:
Original post by Jiia
But protected is still very useful to include tasks for that class to run on itself. This really does make a lot of sense.


I'm not sure what your saying here.

Quote:
Original post by Jiia
I don't seem to use pure interface classes very often. My interface classes usually contain data which is needed for all derived objects. Such as CObject, which has a position vector, which is needed by CCharacter and CItem. That's a simple example; the number of actual variables is pretty high. My code is written now in such a way that CObject has member functions which handle almost everything that both characters and items need to have handled. There are very few functions which are overridden. Most are virtual, and the character and item classes use many CObject member functions to perform their tasks.


Maybe your using the wrong levels of abstraction, i see having CObject type which is the root of all types in a type hierarchy not very useful at all to be honest, it's to abstract to be put into any good use, you end up stuffing data and pushing functionality there where it doesn't actually make any sense.

You usually find interfaces in the concepts of the problem domain, where you see adjective-nouns such as serializable, cloneable, renderable etc or in traditional object-oriented abstract data types (ADTs). Interfaces do not have data members, constants are fine though.

Quote:
Original post by Jiia
There are some situations where not having data as public or protected would seem to be a real pain. My CObject class has a CBitflag variable. CBitflag is a correctly encapsulated class that has member functions to handle tasks with it's own bitflag variable. How do I allow characters to turn on and off state-flags without having to write the CBitflag class interface into the CObject class interface? Example:
*** Source Snippet Removed ***
There are many flags defined for FStatus. Well... 32 :) And this is just an example; I know I could allow CObject to handle deaths, but this is not the case with all flags. And I cannot access them from CCharacter if FStatus is private. What can I do?


solution look at the standard library bitset and prefer using that instead

Share this post


Link to post
Share on other sites
Quote:
Original post by Jiia
I almost forgot to ask one other question. Why derive classes as public? As in CCharacter : public CObject. What happens when you derive them private or protected? Again, sorry if this is a dumb question.


;-)

Share this post


Link to post
Share on other sites
Quote:
Original post by snk_kid
Maybe your using the wrong levels of abstraction, i see having CObject type which is the root of all types in a type hierarchy not very useful at all to be honest, it's to abstract to be put into any good use, you end up stuffing data and pushing functionality there where it doesn't actually make any sense.

I'm not sure what you mean. CObject represents a map based object. I don't see it as abstract at all. My point was to allow any number of map object types to be juggled around in all of my routines without ever really knowing or caring what is what. Most routines treat all map objects exactly the same, but their internal behavior is what makes them different.

Quote:
Original post by snk_kid
solution look at the standard library bitset [www.sgi.com] and prefer using that instead.

Thanks, but templated classes make me rather ill. And I'm not sure how this would help me, as it is the concept I'm asking about, not bitflags.

Share this post


Link to post
Share on other sites
Quote:
Original post by snk_kid
Quote:
Original post by Jiia
But protected is still very useful to include tasks for that class to run on itself. This really does make a lot of sense.


I'm not sure what your saying here.


If I may speak for Jiia, protected member functions are a possible way to implement callbacks. Example, you can publish a "MessagePump" framework class, and give it a protected virtual function OnIdle. The default implementation does nothing, but people may derive from "MessagePump" and implement their own version of OnIdle. The base class code will then call the derived function during its normal processing.

That being said, I wouldn't do anything this way anymore; I'd probably leave it to functors which would get registered with the class, and publish default functors with my nifty MessagePump.

Share this post


Link to post
Share on other sites
Quote:
Original post by Jiia
Quote:
Original post by snk_kid
Maybe your using the wrong levels of abstraction, i see having CObject type which is the root of all types in a type hierarchy not very useful at all to be honest, it's to abstract to be put into any good use, you end up stuffing data and pushing functionality there where it doesn't actually make any sense.

I'm not sure what you mean. CObject represents a map based object. I don't see it as abstract at all. My point was to allow any number of map object types to be juggled around in all of my routines without ever really knowing or caring what is what. Most routines treat all map objects exactly the same, but their internal behavior is what makes them different.


Probably calling it CObject may have miss-lead me. You get people having something like an Object type as root of all types in a hierarchy like in java & objective-c, i just dont think it's very useful to base a design on that because an Object is to abstract but thats not what your CObject type represents so don't worry.

Share this post


Link to post
Share on other sites
I think i should explain what i mean by to abstract to be derived directly from.

Say your problem domain is GUIs and you where focusing on implementing a hierarchy of widgets. Lets start with having a root type for these widgets say you already have an abstract type called Object you use for other problem domains, okay fine we sub-type are widgets from Object type.

Later on you discover that widgets need to implement some basic collision detection so you create a user-defined type that represents the concept of bounds and has an operation that can detect if two bounds have intersected. You've done that you relize that this functionality needs to be through-out the hierarchy of widgets so you end up puting a bounds data member in the Object type and an operation that allows querying/updating/testing etc on the bounds member.

There is one problem does it really make sense for the type Object to have bounds? that depends on which context object is in, the meaning of object is to abstract, it's ambiguous. A much better abstraction would be either to start with an abstract type called widget as the root type, or add a new abstract type derived from the object type also called widget where it makes sense to put a bounds member inside a widget type.

[Edited by - snk_kid on August 3, 2004 6:26:26 PM]

Share this post


Link to post
Share on other sites
Quote:
Original post by Jiia
Thanks, but templated classes make me rather ill. And I'm not sure how this would help me, as it is the concept I'm asking about, not bitflags.


Sorry i wasn't paying much attention on that bit, well i'm bored at the moment so i'll read that bit a bit more closely.

Well you could provide an accessor function that is protected if it doesn't make sense for it to be in the type's interface e.g.


class CBitFlags;

class CObject {
...
CBitFlags FStatus;
...
protected:
const CBitFlags& getFlags() const { return FStatus; }
CBitFlags& getFlags() { return FStatus; }
public:
virtual ~CObject() {}
};

struct CCharacter : public CObject {

void TakeHealth(int value) {
HurtMe(value);
if(GetHealth() < 0)
getFlags().TurnOn(STATUS_DEAD);
}
};








I know your probably thinking what difference does it make but this is just silly example of what can do.

Share this post


Link to post
Share on other sites
No, I don't derive everything from one type. Kind of pointless, really. CObject only has about 4 or 5 derived children.

In my opinion, GetFlags().TurnOn(STATUS_DEAD) is much uglier than having such protected member variables. Not to mention that functions need to be made to return every interface implimented.

In cases such as this, where the data itself is an interface, I don't understand the point of hiding the data in the first place. I'm not saying it's right, but I'm saying I don't understand why it's wrong.

I also find it nice to have sub-interfaces for related tasks, because it leaves the CObject class less cluttered. For example, I could have a very detailed vector class for force, which does its own bounds checking and updates it's own velocity. All objects can have force, but only characters apply forces to themself. So it only makes sense to allow them to access that interface. That, or privide routines specific to characters in the CObject class. But doesn't this go against your original CObject argument? To not provide extensive routines that are specific to only a single or few derived types? Yet again, I'm giving bad examples; I'm sure you understand what I mean, though.
Quote:

If I may speak for Jiia, protected member functions are a possible way to implement callbacks. Example, you can publish a "MessagePump" framework class, and give it a protected virtual function OnIdle. The default implementation does nothing, but people may derive from "MessagePump" and implement their own version of OnIdle. The base class code will then call the derived function during its normal processing.

That being said, I wouldn't do anything this way anymore; I'd probably leave it to functors which would get registered with the class, and publish default functors with my nifty MessagePump.

The HurtMe() function in my example is what I'm speaking of. HurtMe would be a CObject method, as all CObject types can be hurt. So why not have that as protected? I am the absolute master at dumb examples, but I hope the idea is clear.

Share this post


Link to post
Share on other sites
Quote:
Original post by Jiia
No, I don't derive everything from one type. Kind of pointless, really. CObject only has about 4 or 5 derived children.

In my opinion, GetFlags().TurnOn(STATUS_DEAD) is much uglier than having such protected member variables. Not to mention that functions need to be made to return every interface implimented.


In cases such as this, where the data itself is an interface, I don't understand the point of hiding the data in the first place. I'm not saying it's right, but I'm saying I don't understand why it's wrong.


I'm sorry but like i said before that getFlags wasn't such good example to get the point across.

Think about it thou don't you think that having a data member protected is bit of a contridiction like i was saying in my first post above, by declaring a data member protected your implying that unrelated types/modules can't access but if you sub-type you can, if you do that it may aswell be public but then you need to make sure that the member never changes to avoid updating all other modules and re-compilling & the state invariant is maintained manually.

If the representation really is not going to change ever then make it public because making it protected is silly. Just remember thou that this makes it less extendable in the future but that doesn't mean it's a bad thing because to sub-type from certain concrete types would be plain dumb like sub-typing from the c++ standard library complex type.

So with that in mind i would say only

Quote:
Originally by snk_kid
encapsulate the variant/variation.


What do i mean there, well if a representation or even an interface varies statically/dynamically encapsulate it and provide a uniform interface that never changes. This is simillar to the Handle-Body idiom

This is the key to the abstract mechanism, to abstract complexity or variation.

Share this post


Link to post
Share on other sites
I don't see a big difference between an interface data member and a group of member functions. No data can be modified directly. But you still wouldn't always want outside code to be able to call functions of the interfaces, just as you wouldn't want them to be able to call member functions. You don't agree that this situation happens, often?

A) All or most derived types of the base class must perform a task the exact same way.

B) That task may be implimented in different situations / at different times for each derived type. The tasks may be proceeded and followed by other tasks unique to each type. ie: The base class does not know when the tasks must be executed.

C) No outside routines should ever be allowed to run the tasks, as it would screw up the whole flow of things.

If code is broken up properly, there should be quite a few medium sized functions and interfaces which perform individual tasks. Where do I put these functions and interfaces?

Share this post


Link to post
Share on other sites
Quote:
Original post by Jiia
I don't see a big difference between an interface data member and a group of member functions.


An interface type doesn't have any data members, or any implementation whats so ever (in C++ this would be class with no data members and all methods are pure virtual member functions, in java they have interface types, in objective-C there are protocol types). It just defines a protocol for two parties to participate & be able to communicate with each.

Then there is also the object/instance's interface which is something kind of different that would mean all it's public members.

I think that might the problem because when i was saying interface before i was using it to mean either depending on the context being used.

Quote:
Original post by Jiia
No data can be modified directly.


Not sure what you mean, if you make it public, create an instance of that type you can access it directly your code becomes dependant/coupled tightly with it.

Quote:
Original post by Jiia
But you still wouldn't always want outside code to be able to call functions of the interfaces, just as you wouldn't want them to be able to call member functions.


You've completely lost me here, what interface do you mean, an interface type or the inteface of an intance, it's public members that is.

Quote:
Original post by Jiia
You don't agree that this situation happens, often?

A) All or most derived types of the base class must perform a task the exact same way.

B) That task may be implimented in different situations / at different times for each derived type. The tasks may be proceeded and followed by other tasks unique to each type. ie: The base class does not know when the tasks must be executed.

C) No outside routines should ever be allowed to run the tasks, as it would screw up the whole flow of things.

If code is broken up properly, there should be quite a few medium sized functions and interfaces which perform individual tasks. Where do I put these functions and interfaces?


What do you mean by task?, please be abit more clear.

I'm sort of lost wheather your last post was for or against this discussion.

Share this post


Link to post
Share on other sites
If an interface has no data members, then I didn't mean to suggest interfaces. I'm talking about encapsulated classes which are used to maintain specific data and perform specific tasks. They really only serve as an interface, since the class which uses them as data members could easily contain the data instead.

By no data can be directly modified, I'm talking about the individual sub-classes as above. Their data is private, and they do the work on it. The object which uses the class does not. The object which uses the class calls it's interface functions. The derived classes also need to work with the sub-classes. But sometimes, the outside code (outside of the base object's hierarchy altogether) should not be able to mess with it.

That doesn't make sense?

Share this post


Link to post
Share on other sites
How is this:

class Routine
{
public:
VOID DoMethod() { Data++; }
private:
int Data;
};

class Object
{
public:
Routine MyRoutine;
};


Any different than this?

class Object
{
public:
VOID DoMethod() { Data++; }
private:
int Data;
};



The same data members are protected in the same way, are they not? Why is one right and one wrong?

Share this post


Link to post
Share on other sites
Quote:
Original post by Jiia
How is this:
*** Source Snippet Removed ***
Any different than this?
*** Source Snippet Removed ***

The same data members are protected in the same way, are they not? Why is one right and one wrong?


Well it really depends on the requirements but there is alot of a difference the first version of type Object exposes it's interal representation no client (unrelated or related type/module) should know or care to know how it's representated. If any unrelated/related type/module uses that member and you decide to change it to the second version of type Object you will need to update all clients & recompile.

The second version of type Object is much better because every related/unrelated type/module rely on the interface, the "DoMethod" memeber that is. You could easily change the data member int data to the type Routine and not effect all clients, no recompile for the clients just for the type Object.

Another thing it's not just enough to maintain a state invariant in one type, all types should maintain there own state invariant if they have one.

Share this post


Link to post
Share on other sites
By the way if you do this:


class Routine {
public:
Routine(int i = 0): data(i) {}
Routine(const Routine& r): data(r.data) {}
Routine& operator=(const Routine& r) {
if(this != &r) {
data = r.data;
}
return *this;
}

void do_method() { ++data; }

private:
int data;
};

class Object {

Routine* my_routine;

public:

Object(int i = 0): my_routine(new Routine(i)) {
}
Object(const Object& o): my_rountine(new Routine(o.r)) {}

Object& operator=(const Object& o) {
if(this != &o) {
delete my_routine;
my_routine = new Routine(o.r);
}
return *this;
}

void do_method() { my_rountine->do_method(); }

~Object() { delete my_rountine; }
};




If you look at the do_method in type Object this is form of and known as delegation and is a powerful technique.

Share this post


Link to post
Share on other sites
Quote:
Original post by snk_kid
Well it really depends on the requirements but there is alot of a difference the first version of type Object exposes it's interal representation no client (unrelated or related type/module) should know or care to know how it's representated. If any unrelated/related type/module uses that member and you decide to change it to the second version of type Object you will need to update all clients & recompile.

I'm still a little lost. What do you mean by "second version of type Object"? The only thing Object type is exposing is the name of the interface, eg "MyRoutine". Any external routines would simply ObjectType.MyRoutine.DoMethod(); How is this any different than ObjectType.MyRoutine()? I don't see any more danger in the "MyRoutine" variable name changing than the DoMethod() name. I could just as easily want to rename the MyRoutine() function into YourRoutine(). Everything still needs to be updated. The routine class respresents an entire set of common tasks, and in my opinion, is no different than a set of functions. Except that it is much more organized, easily managed, because all related methods and data's are grouped into one sub-object.

Unless you're talking about the fact that you can overload DoMethod() in derived types, but can't really do so, easily, with the Routine class member. If so, in my case I know that these routine classes will not be overloaded.

Quote:
Original post by snk_kid
The second version of type Object is much better because every related/unrelated type/module rely on the interface, the "DoMethod" memeber that is. You could easily change the data member int data to the type Routine and not effect all clients, no recompile for the clients just for the type Object.

The data member is very specific to the Routine class. It would never change unless the Routine class itself needed to change. In which case, there would be little difference in your above example. I just modify the Routine class data member, and not effect all clients, no recompile for the clients, just for the types Routine & Object.

Let me know if I'm totally missing the point.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this