Variable Exposure - Epidemic Alert

Started by
35 comments, last by Holy Fuzz 19 years, 9 months ago
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?
Advertisement
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?
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.
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.
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.
Quote:Original post by Jiia
Let me know if I'm totally missing the point.


I don't mean to be rude but i think you are or you may have misunderstood whats been said. There isn't any more to say from everyone & myself that hasn't already been said. All i can suggest is to either re-read what everyone & myself have written in this thread or find an online article or book on OO anaylses & design or even a recent book on data structures & algorithms thats geared towards OO. Then i suggest you look at design patterns especially the book Design Patterns: Elements of Reusable Object-Oriented Software. I'm sorry i couldn't help you to understand.
As far as I'm concerned, classes are like cars. When you get in a car, you interface with it via the pedels and steering wheels, and expect it to work perfectly. It's got this big, complicated engine under the hood, but you don't care about that, you just want it to go where you tell it to go.

That is, unless you're trying to make the car better. If you want, you can open the hood and muck around with the engine. You'll certainly violate your warrenty, but you just might end up with a new kind of car, based on the old kind of car, that does more stuff, or is better at doing something more specific, like driving through alligator ponds. Or maybe you mucked the whole thing up so bad that it simply explodes on you. Either way, you took the risk of opening the hood and understood the consequences.

To me, this is exactly like protected members. Protected stuff is inaccessible by those who simply want to use the class (just like someone who only wants to drive the car), but if you want to use the class as a foundation to make something better (a derived class), then you violate whatever warrenty comes with the class, and may or may not make it better. It's a risk you choose to take. By giving a class protected members, your saying, "Here, look at me! Extend me and make me better!"

Basically, protected members give you the flexibility to muck with the internals of a class whenever you choose to extend the class into something new. Sure, we could write a sort of "protected interface" that allows derived classes to access, through an interface, those members, but that's a lot of work, and it may or may not be worth it, and may not give you the flexibility needed to come up with the most creative solutions. I realize that this breaks OO forms of thinking, but OO isn't God; like any programming paradigm, it should be used when it helps you and discarded when it doesn't. In the end, it's up to the programmer to decide when it's useful and when it isn't.

My ten cents,
- Fuzz
As far as I can tell, I've understood all of the points made, and thought I have shown that they don't hold up in this situation. If you care to take the time to show me where I didn't prove this, I will attempt to explain more clearly.

As for compile times, by just including a sub-class within the private section, you must recompile that object whenever the sub-object changes. So this is no different than a public Routine class. Unless you consider the method of hiding the class (which is forwardly declared) as an init()-allocated pointer. But since about 80% of data members in most classes are classes themselves, I don't see the point. It just complicates code further to speed up compiles.

If anyone can throw me a scenario where this tactic will have worse results than any other approved programming practice, I'll shut the hell up.

LOL @ Fuzz! You did that just for a rating, didn't you? It was a damn good analogy, so you get it.
Quote:Original post by Holy Fuzz
To me, this is exactly like protected members. Protected stuff is inaccessible by those who simply want to use the class (just like someone who only wants to drive the car), but if you want to use the class as a foundation to make something better (a derived class), then you violate whatever warrenty comes with the class, and may or may not make it better. It's a risk you choose to take. By giving a class protected members, your saying, "Here, look at me! Extend me and make me better!"


No it doesn't, the whole point of inheritance is to extend behaviors/services not representation.

Again i have to say by declaring a data member protected is a complete contradiction and if you seriously believe it should be protected then you may aswell make it public.

[Edited by - snk_kid on August 5, 2004 5:09:32 PM]
It's possible HolyFuzz was speaking generically; obviously his intention. Variables are "being protected" in both protected and private.

What if you need a method to call to perform something that shouldn't be allowed to be accessed anywhere but from a derived type?

No offense, but are you sure you've actually tried to use these techniques to build something complex enough to realize the issues?

This topic is closed to new replies.

Advertisement