Gigantic class
There are no standardized methods for measuring the size of a class.
However, many small specialized classes are preferred to a bulky
single class that contains hundreds of member functions and data
members. But bulky classes do get written. Class std::string has a
fat interface of more than 100 member functions; clearly this is an
exception to the rule and, to be honest, many people consider this to
be a compromise between conflicting design approaches. Still,
ordinary programs rarely use all these members. More than once, I’ve
seen programmers extending a class with additional member functions
and data members instead of using more plausible object-oriental
techniques such as subclassing. As a rule, a class that extends a
20-30 member function count is suspicious.
Gigantic classes are problematic for at least three reasons: users of
such classes rarely know how to use them properly, the implementation
and interface of such classes tend to undergo extensive changes and
bug-fixes; and they are not good candidates for reuse because the fat
interface and intricate implementation details can fit rarely only a
very limited usage. In a sense, large classes are very similar to
large functions.—they are noncohesive and difficult to maintain.
[end of quote]
How do you solve to divide into hundreds of subclasses from one giant
class? Inheritance is the answer, but you write derive class to use
**some** or **all** inherited member functions. You don’t want client
to use all inherited member functions. They only need to derive class
through private inheritance.
For example:
class A
{
public:
A() : a( 1 ), b( 2 ) {}
~A() {}
protected:
int a;
int b;
};
class B : public A
{
public:
B() : c( 3 ), d( 4 ) {}
~B() {}
protected:
int c;
int d;
};
class C : public B
{
public:
C() : e( 5 ), f( 6 ) {}
~C() {}
protected:
int e;
int f;
};
class D : private C
{
public:
D() : g( 7 ), h( 8 ) {}
~D() {}
void Run()
{
a += 100;
b += 200;
c += 300;
d += 400;
e += 500;
f += 600;
g += 700;
h += 800;
}
protected:
int g;
int h;
};
class E : public D
{
public:
E() : i( 9 ), j( 10 ) {}
~E() {}
void Run()
{
D::Run();
i = 900;
j = 1000;
}
private:
int i;
int j;
};
int main()
{
E e; // object e is the interface
e.Run(); // Run() is available to client.
Return 0;
}
Do you see private inheritance? All data members and member
functions are inherited down to class C and all of them are
inaccessible in class D. All data members and member functions are
inherited down to class E from class D.
The client can use only class D’s data members and member functions
that are already inherited in class E.
My example above is object-based programming. All data members and
member functions are grouped in only one big class through
inheritance.
What if you are going to say? Use object-oriental programming
instead? Base class is the general and all derived classes are
specialized.
For example, base class is the CPU’s **ALL** instruction sets. It
has internal registers as data members and pure virtual member
function Dispatch(). Each derived class have specialized instruction
sets and Dispatch() implementation.
Why should each derived class be instanstized and data members are
inherited from base class? You only need one instance like I
described that giant class has one instance.
It is hard to reason about classes named A, B, and C, because that
makes it very abstract. A real class hierarchy usually is a model of
"something", where the structure of "something" often helps in
designing the model.
Most often it helps if a class does one thing, and one thing only. It
should do its "thing", and possibly maintain an internal state that it
might need (an invariant). Having protected data members breaks this,
as the class can no longer take responsibility for its own state.
Therefore protected data is very rarely used.
Unlike Java, in C++ there is generally no requirement for a single
base class. Using templates, all classes conforming to an interface
can be used as arguments to a function.
Bo Persson
Class A has all responsibility to handle private data members and
private member functions. Derived class B, C, and D do not make any
senses. You have no reason to inherit class A's private data members
to class B through additional new class A's public member functions.
I am like class A designer does not want clients to use inheritance
and they only need to define and use class A in their own function
body.
> Most often it helps if a class does one thing, and one thing only. It
> should do its "thing", and possibly maintain an internal state that it
> might need (an invariant). Having protected data members breaks this,
> as the class can no longer take responsibility for its own state.
> Therefore protected data is very rarely used.
I agree with you. Class A does only one thing. One thing has some
internal states. Some internal states are defined in private data
members inside class A. All private member functions maniuplate
internal states directly. Some private member functions are added in
public member functions and the client only needs to invoke public
member function.
The client is allowed to derive class from class A, but they are
denied if they attempt to inherit private data members into derived
class B because protected data is not defined.
> Unlike Java, in C++ there is generally no requirement for a single
> base class. Using templates, all classes conforming to an interface
> can be used as arguments to a function.
Sometimes, I do not need to add templates to class A. The client only
needs to use one data type such as int when they put arguments into
public member function's parameters. They can define one or more
class A objects in their function body. Also, they can use STL such
as vector. They put more class A objects in vector like five class A
objects in one array. The vector has template because class A is
defined user type.
For example:
int variable = 100;
A a1( variable );
A a2( variable + 1 );
A a3; // use default constructor as a3()
A oneArray[5];
vector < A > ( oneArray, 5 );
Make sense?
"god class"? Are you referring that giant class A is perfect with
free bugs unless you have already debugged and tested and it is to be
working properly?
> A typical cell size is 10 µm, but the ostrich egg cell is
> 15 centimetres (5.9 in) long, 13 centimetres (5.1 in) wide,
> and weighs 1.4 kilograms (3.1 lb).
>
> >class? Inheritance is the answer, but you write derive class to use
>
> This is just one answer. And also seen to be an anti-pattern by
> some (as implementation inheritance - not interface inheritance).
If inheritance is not the answer because protected data members and
protected member functions are not defined, what is the solution? You
might say that composition is the answer. Create hundreds of base
subclasses. Put them into one base main class. How can base
subclasses access base main class' private data members?
I showed you my example as class A, B, C, and D through public
inheritance above. Convert from inheritance to composition. Let's
say for example. class B, C, and D are considered to be base
subclasses and class A is considered to be base main class. Class B,
C, D can't access class A's private data members unless you add friend
class B, C, and D in class A. Class A needs to access class class B,
C, and D's member functions before class B, C, D's member functions in
turn to access class A's private data members directly.
class B { public: /* member functions access A's private data members
*/ };
class C { public: /* member functions access A's private data members
*/ };
class D { public: /* member functions access A's private data members
*/ };
class A
{
friend class B
friend class C
friend class D
public: /* member functions access class B, C, D's member functions */
private: /* private internal states as data members */
B b;
C c;
D d;
};
Class A's data members are always undefined because class A body is
not declared in the top before class B, C, and D are defined. I guess
that composition is not the answer. How do you have the solution?
You refer "Refactoring". Do you mean divide one big problem is
divided into several small problems? I showed quote from ANSI/ISO
book above. Gigantic class is like a big function body. You create
one thing. One thing is one problem. You can't solve it at once.
You need to write small sub-functions and focus small problems. All
sub-functions are debugged and tested correctly before you put them
together in one main function.
If a bug is found, it will *affect* some or all sub-functions. Trace
every sub-functions until you found a bug. Fix one sub-function
before all sub-functions in one main function are working and running
correctly. All sub-functions do not need to be modified.
> (Of course, I cannot promise to solve every problem posted to
> Usenet, I just might try and do it for some small problems
> if I have some leisure.)
>
> The meaning of »god class« can be looked up using a search
> engine.
I am unable to find *god class* in Google search, but I do see *God
object*. I think that you are referring "C++ Anti-Pattern Design". I
have good understanding how C++ language is written, but I probably do
not have experience to understand Pattern Design.
For starters he means this:
http://www.refactoring.com/catalog/index.html
and this
http://en.wikipedia.org/wiki/Code_refactoring
Use those strategies to "pull out" code from your god object into
more compact classes.
> I am unable to find *god class* in Google search, but I do see *God
> object*. I think that you are referring "C++ Anti-Pattern Design".
That's right.
--Jonathan
Jonathan,
Thank you for the information. It is very new to me and I am learning
by reading reference. You provided me two websites, but I found good
website at http://sourcemaking.com. It gives you the information
about Design Pattern and Refactoring. You can click Refactoring and
you will see reference.
there is someone that in few line of C++ code (that runs)
can show (or better make the debugger show)
the useful of that words?
it seems to understand the word "protected" could be
useful for not make happen wrong operator overloading
or something of that, right?
thanks
--------------------------------------------
#include <iostream>
#include <list>
#include <string>
#include <cstdio>
#define P printf
using namespace std;
class A
{public:
A():a(1),b(2){}
~A(){}
int a;
int b;
};
class B : public A
{public:
B():c(3),d(4){}
~B() {}
int c;
int d;
};
--------------
a=101
b=202
c=303
d=404
e=505
f=606
g=707
h=808
i=900
j=1000
class C : public B
{public:
C():e(5),f(6){}
~C(){}
int e;
int f;
};
class D : public C
{public:
D():g(7),h(8){}
~D() {}
void Run()
{a+=100;
b+=200;
c+=300;
d+=400;
e+=500;
f+=600;
g+=700;
h+=800;
}
int g;
int h;
};
class E : public D
{public:
E():i(9),j(10){}
~E(){}
void Run()
{D::Run();
i = 900;
j = 1000;
}
void print()
{P("a=%d\n", a);
P("b=%d\n", b);
P("c=%d\n", c);
P("d=%d\n", d);
P("e=%d\n", e);
P("f=%d\n", f);
P("g=%d\n", g);
P("h=%d\n", h);
P("i=%d\n", i);
P("j=%d\n", j);
}
int i;
int j;
};
int main()
{E e; // object e is the interface
e.Run(); // Run() is available to client.
e.print();
return 0;
}
You may want to lookup "One Responsibility Rule"
http://c2.com/cgi/wiki/Wiki?OneResponsibilityRule
>
> How do you solve to divide into hundreds of subclasses from one giant
> class? Inheritance is the answer,
Not necessarily. I mainly use composition.
From a design point of view, I try to keep inheritance for *extending*
functionality.
[snip code]
> My example above is object-based programming.
Is it ? In what sense ?
http://en.wikipedia.org/wiki/Object-based
> All data members and
> member functions are grouped in only one big class through
> inheritance.
> What if you are going to say? Use object-oriental programming
> instead? Base class is the general and all derived classes are
> specialized.
As you want to use it, this is not OOP. While the wording is good,
inheritance is not taken that way (see later).
> For example, base class is the CPU’s **ALL** instruction sets. It
> has internal registers as data members and pure virtual member
> function Dispatch(). Each derived class have specialized instruction
> sets and Dispatch() implementation.
Taking your example, in OOP, the base class would be a x86 processor
(with only x86 instruction set) while an inherited class would be a
specific class of x86 compatible processor (let's say with MMX
support).
It doesn't mean that all additional MMX functions must be represented
as a virtual function in the base class. Inheritance is 'MMX
processor' *reusing* code from 'x86 processor' and *extending* it.
> Why should each derived class be instanstized and data members are
> inherited from base class? You only need one instance like I
> described that giant class has one instance.
I don't understand your point here.
I think you are trying to make a big polymorphic class, redefining the
functions for implementation and end up having to instantiate the most
derived class. If it is the case, IMO you have a design problem here.
--
Michael
io_x wrote:
>
> "Immortal Nephi" <Immorta...@hotmail.com> ha scritto nel messaggio
> news:dcc957fe-3558-4f5b...@u1g2000pre.googlegroups.com...
>>I consider to use either object-based programming or object-oriental
> ...
> i find the word "private" and "protected" not useful
> like the word "const".
> in my code where is the problem?
Have you ever used the words "private" and "const" ?! Do you know the
meaning of them? Do you know that using "const" can help the compiler to do
better optimizations?
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (GNU/Linux)
iEYEARECAAYFAks7L1cACgkQG6NzcAXitM+ljwCeKI/Bjv3IsUL2LNjcrsf66HNF
WPUAoIsllkIjSIj0R5Egsmat0DklGj+z
=D5CL
-----END PGP SIGNATURE-----
I suspect that in practice, there is very little optimization the
compiler can make (even if mutable field didn't exist); in most cases
it can apply optimization by itself, detecting that data is not
modified.
There could be some difference in the location of some const data.
--
Michael
yes i used them ("private", "const", "protected");
> Do you know the meaning of them?
not 100%, but i know how use them (with some help of the compiler)
> Do you know that using "const" can help the compiler to do
> better optimizations?
no i don't know it...
it seems i find problem in remember in general,
big languages standards in particular.
i prefer speak in the beginning from runnable example
(the runnable example i have some problem to find (cause to few imagination?))
and do some analisys on it for see if i find some way better for
not using these above words ("private", "const", "protected");
for "const" i'm 99% sure is not useful
The answer depends on context of your problem.
You should be solving this problem in problem space, not coding space.
By that I mean, you don't solve a problem about, to pull a name
out of the air, traffic control, by thinking about public vs
protected inheritance, or how many lines of code should be in
a class. You solve it by identifying the classes of object in
your problem space, and identifying how they behave and interact.
Then to design your objects, you think of them as exporters
of functionality and behaviour. If the object in your problem
space is big and interacts with many other objects, then it
*may* be the case you want a big object to represent it.
Or it may be you want to re-examine your problem space and
make the problem fit into an easier to understand model.
So, to pull that example back in, in traffic control you think
about objects like cars, roads, traffic signals, emergency
vehicles, etc. and so on. These will have behaviours in
the model that is used to work on the problem. You want to
make objects in your code that behave that way. Hopefully
you can manage complexity through the usual choices, like
layers and hiding information and so on.
Socks