 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Ernst Baumann Guest
|
Posted: Fri Mar 09, 2007 10:38 pm Post subject: Einfache Design-Frage (Oberfläche - Fachklassen) |
|
|
Hallo allerseits,
1) In einem Feld sollen sich in den einzelnen Feldelementen (durch
einen Zufallsgenerator) zufälligerweise Rechtecke und Kreise befinden,
von denen dann über eine Schleife anschließend die Daten der Rechtecke
bzw. Kreise auf dem Bildschirm ausgegeben werden sollen.
Dies habe ich mit einer virtuellen Methode, die ich print() genannt
habe gemacht.
Das habe ich alles (in einem Demoprogramm) auch hinbekommen. Quellcode
siehe unten.
2) Doch dann hat mir jemand was von einer 4 Schichten Architektur
erzählt, bei der man zwischen der Schicht Oberfläche und der Schicht
Fachklassen trennen muss.
D.h. ich kann also in meiner Fachklasse nicht mehr die Methode print()
basteln, in der man direkt etwas auf dem Bildschirm ausgibt, sondern
muss eine Methode, ich nenne sie mal getInfos() basteln, die die
- Breite,
- Länge
- und die Zeichenkette "Rechteck" zurückgibt
falls es sich um ein Rechteck handelt und die
den
- Radius
- und die Zeichenkette "Kreis" zurückgibt,
falls es sich um einen Kreis handelt.
Und in der Funktion main() kann man diese Methode getInfos() dann
benutzen, um dort mit cout bzw. einer selbstgebastelten Metode z.B.
myprint genannt, dann diese Daten auf dem Bildschirm ausgeben.
Frage:
Wie macht man das am einfachsten und geschicktesten?
Es gibt dazu sicherlich eine empfohlene Technik (selber hinschrubben
könnte ich das auch irgendwie, aber ich weiß dann nicht, ob man das
dann so macht ...)
--------------------------------------------------------------
Compiler: MSVC++ Vers. 6.0
--------------------------------------------------------------
#include "stdafx.h"
#include <iostream>
#include <time.h>
using namespace std;
class Form{
public:
virtual void print()=0;
Form();
~Form();
}; // Form
void Form::print(){
printf("Diese Form ist ein ... ");
};
Form::Form(){
};
Form::~Form(){
};
class Rechteck: public Form{
private:
double l;
double b;
public:
Rechteck(double ll, double bb);
Rechteck::~Rechteck();
void print();
};
Rechteck::Rechteck(double ll, double bb){
l = ll;
b = bb;
};
Rechteck::~Rechteck(){
};
void Rechteck::print(){
cout << "Rechteck: Laenge = " << l << " Breite = " << b <<
endl;
};
class Kreis: public Form{
private:
double r;
public:
Kreis(double rr);
Kreis::~Kreis();
void print();
};
Kreis::Kreis(double rr){
r = rr;
}
Kreis::~Kreis(){
}
void Kreis::print(){
cout << "Kreis: Radius = " << r << endl;
}
int main(){
int r;
int i;
Form *f[10];
srand( (unsigned)time( NULL ) );
// Feld mit Hilfe des Zufalls mit Kreisen oder Rechtecken füllen
for(i=0;i<10;i++){
r = rand()%2;
if(r==0){
f[i] = new Kreis(100*i);
}
else{
f[i] = new Rechteck(10*i, 20*i);
}
}
// Jedes Element des Feldes ausdrucken
for(i=0;i<10;i++){
f[i]->Form::print();
f[i]->print();
}
// dynamischen Speicher wieder freigeben
for(i=0;i<10;i++){
delete f[i];
}
return 0;
}
--------------------------------------------------------------
mfg
Ernst |
|
| Back to top |
|
 |
Torsten Robitzki Guest
|
Posted: Sat Mar 10, 2007 12:08 am Post subject: Re: Einfache Design-Frage (Oberfläche - F achklassen) |
|
|
Hallo Ernst,
Ernst Baumann wrote:
| Quote: | Hallo allerseits,
2) Doch dann hat mir jemand was von einer 4 Schichten Architektur
erzählt, bei der man zwischen der Schicht Oberfläche und der Schicht
Fachklassen trennen muss.
D.h. ich kann also in meiner Fachklasse nicht mehr die Methode print()
basteln, in der man direkt etwas auf dem Bildschirm ausgibt, sondern
muss eine Methode, ich nenne sie mal getInfos() basteln, die die
- Breite,
- Länge
- und die Zeichenkette "Rechteck" zurückgibt
falls es sich um ein Rechteck handelt und die
den
- Radius
- und die Zeichenkette "Kreis" zurückgibt,
falls es sich um einen Kreis handelt.
Frage:
Wie macht man das am einfachsten und geschicktesten?
Es gibt dazu sicherlich eine empfohlene Technik (selber hinschrubben
könnte ich das auch irgendwie, aber ich weiß dann nicht, ob man das
dann so macht ...)
|
Das Stichwort ist hier visitor pattern. Ein pattern, das praktisch ist,
um eine gegebene Objekthirachie um Funktionalitäten zu erweitern, ohne
dabei mit jedem Aspekt die Klassen weiter zu überfrachten.
Vorraussetzung dafür ist, das die Objekthirachie stabil ist und nicht
ständig neue Klassen dazu kommen. Beispiel:
class rechteck;
class kreis;
class output_device;
class vistor {
virtual void visit(const rechteck&) const = 0;
virtual void visit(const kreis&) const = 0;
};
class form {
public:
virtual void visit(const visitor&) const = 0;
virtual ~from() {}
};
class quadrat {
public:
explicit quadrat(double l) : laenge_(l) {}
double laenge() const {return laenge_;}
private:
virtual void visit(const visitor& v) const {v.visit(*this)}
double laenge_;
};
class kreis {
public:
explicit kreis(double r) : radius_(r) {}
double radius() const {return radius_;}
private:
virtual void visit(const visitor& v) const {v.visit(*this)}
double radius_;
};
// Dies hier erweitert die Klassenhirachie um die Möglichkeit, gedruckt
zu werden
class printer : public visitor {
public:
printer(output_device& d) : out_;
private:
virtual void visit(const rechteck& r) const
{
out_.draw_rect(r.leange());
}
virtual void visit(const kreis& k) const
{
out_.draw_circle(k.radius());
}
output_device out_;
};
Über die const qualifier an den visit() Funktionen, kann man sicher
diskutieren.
| Quote: |
--------------------------------------------------------------
Compiler: MSVC++ Vers. 6.0
--------------------------------------------------------------
|
Warum gibt es so viele Leute, die 10 Jahre alte compiler verwenden?
mfg Torsten |
|
| Back to top |
|
 |
Ole Hinz Guest
|
Posted: Sun Mar 11, 2007 2:12 am Post subject: Re: Einfache Design-Frage (Oberfläche - F achklassen) |
|
|
Torsten Robitzki wrote:
| Quote: |
--------------------------------------------------------------
Compiler: MSVC++ Vers. 6.0
--------------------------------------------------------------
Warum gibt es so viele Leute, die 10 Jahre alte compiler verwenden?
mfg Torsten
|
Weil es Firmen gibt, die sehr viel Sourcecode produziert haben, der noch
benutzt wird und die Besonderheiten benutzt.
Teilweise wird der Code neben diesem Compiler auch noch von anderen
Compilern verarbeitet. Um das zu machen, musste man sehr viel
compilerabhängige Bauprozesse einrichten.
Das Umstellen bei umfangreichen Sourcen dauert einfach viel zu lange, so
dass erstmal alle weiter mit der alten Krücke leben müssen.
mfg Ole
--
http://www.ole-hinz.de |
|
| Back to top |
|
 |
Ernst Baumann Guest
|
Posted: Sun Mar 11, 2007 7:23 pm Post subject: Re: Einfache Design-Frage (Oberfläche - Fachklassen) |
|
|
| Quote: | Hallo Thorsten,
vielen Dank für deine Antwort. |
Da ich allerdings kein professioneller bzw. versierter Programmiere
bin, habe ich noch ein paar Fragen:
| Quote: |
Das Stichwort ist hier visitor pattern. Ein pattern, das praktisch ist,
was heißt pattern ? Muster ?
class rechteck;
class kreis;
class output_device;
wo wird die Klasse output_device implementiert?
class vistor {
virtual void visit(const rechteck&) const = 0;
virtual void visit(const kreis&) const = 0;
};
class form {
public:
virtual void visit(const visitor&) const = 0;
virtual ~from() {}
};
Bei mir hieß die Klasse rechteck. |
Warum hast du die Klasse quadrat benutzt?
| Quote: |
class quadrat {
muss Klasse quadrat nicht von Klasse form erben (so wie in meinem |
Programm)?
| Quote: |
public:
explicit quadrat(double l) : laenge_(l) {}
laenge_(l) gehört doch zur Initialisierungsliste. |
Die Initialisierungsliste besteht doch aber aus Konstruktoraufrufen
der Oberklasse. Aber Klasse quadrat hat keine Oberklasse.
Das verstehe ich nicht.
| Quote: |
double laenge() const {return laenge_;}
private:
virtual void visit(const visitor& v) const {v.visit(*this)}
double laenge_;
};
class kreis {
muss Klasse kreis nicht von Klasse form erben (so wie in meinem |
Programm)?
| Quote: |
public:
explicit kreis(double r) : radius_(r) {}
radius_(r) gehört doch zur Initialisierungsliste. |
Die Initialisierungsliste besteht doch aber aus Konstruktoraufrufen
der Oberklasse. Aber Klasse kreis hat keine Oberklasse.
Das verstehe ich nicht.
| Quote: |
double radius() const {return radius_;}
private:
virtual void visit(const visitor& v) const {v.visit(*this)}
double radius_;
};
// Dies hier erweitert die Klassenhirachie um die Möglichkeit, gedruckt
zu werden
class printer : public visitor {
public:
printer(output_device& d) : out_;
out_ gehört doch zur Initialisierungsliste. |
Die Initialisierungsliste besteht doch aber aus Konstruktoraufrufen
der Oberklasse.
Aber Klasse visitor hat keinen Konstruktor out_.
Außerdem braucht ein Konstruktoraufruf syntaktisch eine öffnende
Klammer ( und eine schließende Klammer ).
out_ hat aber weder eine öffende noch eine schließende Klammer.
| Quote: |
private:
virtual void visit(const rechteck& r) const
{
out_.draw_rect(r.leange());
in r, also dem Rechteck kommt die Methode laenge() nicht vor.
}
virtual void visit(const kreis& k) const
{
out_.draw_circle(k.radius());
in k, also dem Kreis kommt die Methode radius() nicht vor.
}
output_device out_;
};
Könntest du mir ein Beispiel für main() angeben, d.h. in main will ich |
die Daten eines Rechtecks (Länge, Breite und dass es sich um ein
Rechteck handelt) und eines Kreises (Radius und dass es sich um ein
Kreis handelt) )auf dem Bildschirm (Konsole) ausgeben (mit cout)
mfg
Ernst |
|
| Back to top |
|
 |
Torsten Robitzki Guest
|
Posted: Mon Mar 12, 2007 12:47 pm Post subject: Re: Einfache Design-Frage (Oberfläche - F achklassen) |
|
|
Ernst Baumann wrote:
Hallo Ehrnst,
| Quote: | Das Stichwort ist hier visitor pattern. Ein pattern, das praktisch ist,
was heißt pattern ? Muster ?
|
Muster. Ja. Hier sei Dir das Buch "Design Patterns; Elements of Reusable
Object-Oriented Software" empfohlen. Gibt es auch auf deutsch, ich
persönlich fand die Übersetzung zumindest ungewöhnlich.
| Quote: |
class rechteck;
class kreis;
class output_device;
wo wird die Klasse output_device implementiert?
|
Ist für das Beispiel unerheblich.
| Quote: | Bei mir hieß die Klasse rechteck.
Warum hast du die Klasse quadrat benutzt?
|
Weil ich freier Softwareentwickler bin und weil ein Quadrat nur einen
member braucht und ich dadurch Tipparbeit gespärt hätte, wenn Du nicht
nachgefragt hättest.
| Quote: | muss Klasse quadrat nicht von Klasse form erben (so wie in meinem
Programm)?
|
Richtig, mein Fehler.
| Quote: | public:
explicit quadrat(double l) : laenge_(l) {}
laenge_(l) gehört doch zur Initialisierungsliste.
Die Initialisierungsliste besteht doch aber aus Konstruktoraufrufen
der Oberklasse. Aber Klasse quadrat hat keine Oberklasse.
Das verstehe ich nicht.
|
In der Initialisierungsliste werden die Basisklassen und die member des
Objektes initialisiert.
explicit quadrat(double l) : form(), laenge_(l) {}
Wobei der default Konstruktor immer aufgerufen wird, wenn man keinen
angibt. Man kann ihn also auch in der Initialisierungsliste weg lassen.
| Quote: | muss Klasse kreis nicht von Klasse form erben (so wie in meinem
Programm)?
|
Jep.
| Quote: | radius_(r) gehört doch zur Initialisierungsliste.
Die Initialisierungsliste besteht doch aber aus Konstruktoraufrufen
der Oberklasse. Aber Klasse kreis hat keine Oberklasse.
Das verstehe ich nicht.
|
S.O.
| Quote: | out_ gehört doch zur Initialisierungsliste.
Die Initialisierungsliste besteht doch aber aus Konstruktoraufrufen
der Oberklasse.
|
S.O.
| Quote: | Aber Klasse visitor hat keinen Konstruktor out_.
|
Jede Klasse hat einen Konstruktor. Wenn Du keinen angibst, erzeugt der
compiler für Dich einen default Konstruktor.
| Quote: | Außerdem braucht ein Konstruktoraufruf syntaktisch eine öffnende
Klammer ( und eine schließende Klammer ).
|
Richtig!
| Quote: | Könntest du mir ein Beispiel für main() angeben, d.h. in main will ich
die Daten eines Rechtecks (Länge, Breite und dass es sich um ein
Rechteck handelt) und eines Kreises (Radius und dass es sich um ein
Kreis handelt) )auf dem Bildschirm (Konsole) ausgeben (mit cout)
|
Das wäre mir jetzt etwas zu konkrett (sprich klingt etwas arg nach
Hausarbeiten), wir könnten aber mein Beispiel etwas erweitern.
int main()
{
output_device out;
rechteck r(1.0, 1.0);
kreis k(1.2);
printer p(out);
printer.visit(r);
printer.visit(k);
}
mfg Torsten |
|
| Back to top |
|
 |
Ernst Baumann Guest
|
Posted: Mon Mar 12, 2007 11:40 pm Post subject: Re: Einfache Design-Frage (Oberfläche - Fachklassen) |
|
|
| Quote: |
Das wäre mir jetzt etwas zu konkrett (sprich klingt etwas arg nach
Hausarbeiten),
Es sind garantiert keine Hausarbeiten. |
Deshalb bin ich froh, dass du mir einfache Beispiele geliefert hast,
damit ich das Prinzip hoffe verstehen zu können.
| Quote: |
wir könnten aber mein Beispiel etwas erweitern.
int main()
{
output_device out;
rechteck r(1.0, 1.0);
kreis k(1.2);
printer p(out);
printer.visit(r);
1) |
muss es nicht p.visit(r) heissen?
| Quote: |
printer.visit(k);
muss es nicht p.visit(k) heissen?
Aber die Mehode visit in der Klasse printer ist private. |
Deshalb kann
p.visit(k)
nicht funktionieren. Wie heißt der Aufruf richtig?
2)
Was mir überhaupt nicht klar ist:
Du hast die Methode visit in den Klassen Rechteck und Kreis gar nicht
benutzt. Warum hast du sie dann implementiert?
3)
Wenn ich aber ein Feld von (zufällige bestimmten) Rechtecken und
Kreisen habe, wie in dem folgenden main() unten.
Wie kann man dann die Attribute dieser Rechtecke und Kreise ausgeben?
Ich will in einer Schleife die Elemente (besser die Attribute davon)
des Feldes f ausgeben, die entweder Rechtecke oder Kreise sind.
Da ich nicht weiß, ob es konkret ein Rechteck bzw. Quadrat ist, habe
ich damit Schwierigkeiten. Das ist mein Problem (siehe unten
printAberWie(f[i]))
Ich verstehe leider den Trick mit visit und visitor nicht, der mir
dabei helfen soll und was mit dem Trick _beabsichtigt_ wird.
---------------------------------------------------------------------
int main(){
int r;
int i;
Form *f[10];
srand( (unsigned)time( NULL ) );
// Feld mit Hilfe des Zufalls mit Kreisen oder Rechtecken füllen
for(i=0;i<10;i++){
r = rand()%2;
if(r==0){
f[i] = new Kreis(100*i);
}
else{
f[i] = new Rechteck(10*i, 20*i);
}
}
---------------------------------------------------------------
---------Wie funktioniert das richtig ??------------
for(i=0;i<10;i++){
printAberWie(f[i]);
----------------------------------------------------
mfg
Ernst |
|
| Back to top |
|
 |
Björn Hendriks Guest
|
Posted: Tue Mar 13, 2007 3:07 am Post subject: Re: Einfache Design-Frage (Oberfläche - Fachklassen) |
|
|
Hallo,
Torsten Robitzki wrote:
| Quote: | Ernst Baumann wrote:
Das Stichwort ist hier visitor pattern. Ein pattern, das praktisch ist,
was heißt pattern ? Muster ?
Muster. Ja. Hier sei Dir das Buch "Design Patterns; Elements of Reusable
Object-Oriented Software" empfohlen. Gibt es auch auf deutsch, ich
persönlich fand die Übersetzung zumindest ungewöhnlich.
|
Einen guten Überblick über die in dem Buch beschriebenen Pattern gibt es
auch in der Wikipedia unter
http://de.wikipedia.org/wiki/Viererbande_(Softwareentwicklung)
Von dort aus gibt es Links zu Wikipedia-Artikeln die jeweils ein
Entwurfsmuster genauer beschreiben. Es lohnt sich, darin mal etwas zu
stöbern. Als Einstieg empfehle ich das Muster Einzelstück (Singleton).
Allgemein kann man sagen, dass Entwurfsmuster beschreiben, wie man
bestimmte, immer wieder auftretende Implementierungsprobleme geschickt
lösen kann.
Gruß
Björn |
|
| Back to top |
|
 |
Torsten Robitzki Guest
|
Posted: Tue Mar 13, 2007 12:31 pm Post subject: Re: Einfache Design-Frage (Oberfläche - F achklassen) |
|
|
Ernst Baumann wrote:
| Quote: | printer.visit(r);
1)
muss es nicht p.visit(r) heissen?
|
Richtig.
| Quote: | printer.visit(k);
muss es nicht p.visit(k) heissen?
|
Richtig.
| Quote: | Aber die Mehode visit in der Klasse printer ist private.
Deshalb kann
p.visit(k)
nicht funktionieren. Wie heißt der Aufruf richtig?
|
In Form ist die Funktion aber public. static_cast<form*>(p)->visit(k)
sollte funktionieren. Ob es ohne den cast funktioniert, da wäre ich im
Moment etwas überfragt. Grundsätzlich soll das private verhindern, das
sich irgendwer direkt der Implementierungsdetails bedient um z.B. eine
neue Form zu implementieren.
| Quote: |
2)
Was mir überhaupt nicht klar ist:
Du hast die Methode visit in den Klassen Rechteck und Kreis gar nicht
benutzt. Warum hast du sie dann implementiert?
|
Wie? In den Beispiel main() werden doch beide Funktionen benutzt.
| Quote: | 3)
Wenn ich aber ein Feld von (zufällige bestimmten) Rechtecken und
Kreisen habe, wie in dem folgenden main() unten.
Wie kann man dann die Attribute dieser Rechtecke und Kreise ausgeben?
Ich will in einer Schleife die Elemente (besser die Attribute davon)
des Feldes f ausgeben, die entweder Rechtecke oder Kreise sind.
Da ich nicht weiß, ob es konkret ein Rechteck bzw. Quadrat ist, habe
ich damit Schwierigkeiten. Das ist mein Problem (siehe unten
printAberWie(f[i]))
Ich verstehe leider den Trick mit visit und visitor nicht, der mir
dabei helfen soll und was mit dem Trick _beabsichtigt_ wird.
|
Nun, die offensichtliche Lösung wäre, das Du in Form eine abstrakte
Funktion einfügst, die dann jeweils unterschiedlich in Kreis und
Rechteck (und was weiß ich alles) implementiert wird. Diese Lösung
wolltest Du aber nicht, sondern wolltest eine Indirektion mehr (so habe
ich Dich zumindest verstanden). Das visitor pattern liefert diese, in
dem letztendlich eine Funktion, abhängig von 2 dynamischen Typen (der
Form und dem Visitor) aufgerufen wird.
| Quote: | ---------------------------------------------------------------------
int main(){
int r;
int i;
Form *f[10];
|
in C++ braucht man Variablen erst dort zu deklarieren, wo man sie benötigt.
| Quote: | srand( (unsigned)time( NULL ) );
|
in C++ gibt es viel genauere casts als den C-cast, den Du hier angewandt
hast. Hier wäre static_cast<> wohl angebracht.
| Quote: | // Feld mit Hilfe des Zufalls mit Kreisen oder Rechtecken füllen
for(i=0;i<10;i++){
r = rand()%2;
if(r==0){
f[i] = new Kreis(100*i);
}
else{
f[i] = new Rechteck(10*i, 20*i);
}
}
|
Dir sollte klar sein, das das nur ein Beispiel sein kann und im realen
Leben die new-Ausdrücke Ausnahmen werfen können und das blanke Array ist
dann nicht schlau genug, die bereits erfolgreich angelegten Elemente
wieder zu zerstören.
| Quote: | ---------------------------------------------------------------
---------Wie funktioniert das richtig ??------------
|
// jetzt nagel mich aber nicht wieder drauf fest, das im original
// Beispiel der Konstruktor noch einen Parameter hat.
printer p;
| Quote: | for(i=0;i<10;i++){
p.visit(*f[i]); |
Für den Fall, das Du hier wieder einen Fehler gefunden hast, dann war
das Absicht, um zu gucken, ob Du noch aufpasst. Ich benutze keinen
Compiler, wenn ich Dir hier ein Beispiel schreibe. Ansonsten solltest Du
dem Tip von Björn noch mal folgen (wobei ich der Meinung bin, das das
Singelton Patter ein schlechtes Beispiel ist).
mfg Torsten |
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|