Akzeptanz unterschiedlicher Proxys für Klasse
-
Genau.
-
Und wieso kannst du nicht nochmal Proxy und Nicht-Proxy die selbe Basisklasse spendieren und in SomeUIClass diese verwenden?
Weil das Proxy nicht zwingend das ganze Interface implementieren muss, sondern manchmal auch nur einen kleinen Satz aus Funktionen von Nicht-Proxy übernimmt!?
-
Weil facepalm das als schlechten Stil angepriesen hat und mich vorläufig auch von seiner Lösung überzeugt hatte - die jetzt jedoch ziemlich unumsetzbar erscheint, wenn man SomeClass und SomeClassProxy so austauschbar nutzen möchte, wie man es eigentlich von einem Proxy gewohnt ist.
Seine Lösung ist halt irgendwie ein CompileTimeProxy. Ich brauche einen RuntimeProxy. Aber dieses Bedürfnis kann ich mit C++ offensichtlich nicht umsetzen, ohne dass es java-style wird. Aber wenn meine Anforderung in Ordnung ist und es ohne Polymorphie nicht geht, muss eben das Interface drüber...
Wobei ich mit dem Interface auch wieder auf Probleme stoße. Da der Stellvertreter ja nur zeigend auf das eigentliche Objekt ist, kann ich darauf schlecht einen Zeiger hingeben, so ein Proxy ist dann ja ziemlich flüchtig, wird oft Mal temporär eingesetzt, ist kopierbar... Ich könnte ihn auch nicht-kopierbar machen, aber dann stellt sich trotzdem wieder die Frage, wer für den Besitz zuständig ist. Ach ich komm gedanklich heute auf keinen grünen Zweig
-
- Kann es eine Proxy-Kette geben? Also P->P->P->NP
- Ist hinterm (letzten) P immer ein NP
- Kann ein P mit einem anderen P ausgetauscht werden
- Müssen die P's dann das selbe NP haben
- ...
-
- Kette: Theoretisch ja, praktisch zurzeit aber nicht notwendig
- Kette endet definitiv mit NP, genau
- da es nur ein P gibt, kann natürlich ein P mit einem anderen ausgetauscht werden; bzw. wenn es später noch einen weiteren P gibt, dann soll auch das gehen
- P dasselbe NP haben verstehe ich nicht ganz, ein Proxy hat ja entweder einen Verweis auf einen anderen Proxy oder auf einen Nicht-Proxy, somit ist klar, dass eine Kette immer auch nur einen NP haben kann
-
Kannst du mir zeigen, wie das hier nicht geht?
class Logger { // oder was das auch machen soll virtual void logDoThis() {} // sehr wahrscheinlich braucht es nur virtual void logThat() {} // eine einzige Methode. Dann nimm aber // lieber std::function }; class RealLogger { virtual void logDoThis() { ... } virtual void logThat() { ... } }; struct LoggedSomeClass { unique_ptr<SomeClass> s; unique_ptr<Logger> l; void doThis() { l->logThis(); s.doThis(); } void doThis() { l->logThat(); s.doThat(); } }; class SomeUIClass { private: unique_ptr<LoggedSomeClass> someClassOrProxy; public: void SetSomeClassOrProxy(unique_ptr<SomeClass> s) { someClassOrProxy.reset(new LoggedSomeClass{move(s), new Logger()}); } void SetSomeClassOrProxy(unique_ptr<LoggedSomeClass> s) { someClassOrProxy = move(s); } void doSomething() { // ... someClassOrProxy->doThis(); // ... } };
Proxyketten lassen sich durch eine Klasse
KettenLogger
abbilden.
-
Ah, ich habe
class RealLogger : public Logger {
vergessen, daher das Missverständnis.
-
Ah okay, das klärt zumindest.
Na ja und sonst ist das mit dem Logger halt eine Vereinfachung von mir gewesen, die nicht so zutrifft.
class SomeClass { public: void meth1(int a); bool meth2(); void meth3(int a, int b); }; class SomeClassTreeProxy { public: SomeClassProxy(SomeClass& someClass, Tree& tree) : /* ... */ void meth1(int a) { // hier wird a vor Weitergabe z.B. geändert a += 10; someClass.meth1(a); } bool meth2() { bool result = someClass.meth2(); // hier wird das Resultat nochmal anhand der Nachbarn/Eltern im Baum geändert return result & /* irgendwelche Abfragen */; } void meth3(int a, int b) { // hier geschieht wiederum etwas vom Baum Abhängiges a -= 5; b *= 2; // beispielhaft natürlich someClass.meth3(a, b); } void meth4() { // das ist sogar eine Zusatzmethode; wer den Proxy nutzt, // hat das noch zusätzlich, weil SomeClass im Baum-Kontext // steht } };
Das ist jetzt wieder abstrahiert und ich hoffe, es wird einfach klar, dass der Proxy nicht einfach NUR filtert oder NUR loggt oder NUR dies und das macht, sondern als Proxy für eine SomeClass dient, die sich im Baumkontext befindet.
Im Baum selbst ist SomeClass abgelegt, wenn man vom Baum aber SomeClass erhalten möchte, erhält man den SomeClassTreeProxy, damit der Proxy brav berücksichtigt, dass SomeClass in einem Baum eben anders agiert.
Erscheint vielleicht erstmal nicht intuitiv, funktioniert aber (mit Polymorphie zumindest) sauber: Wenn ich über den Tree an eine Range gehe und das Resultat jemandem übergebe, wird sichergestellt, dass alle Einfügeoperationen unter Berücksichtigung des Baumes erfolgen.
Dadurch sollte jetzt auch verständlich werden, wieso ich nicht einfach einen std::vectorstd::function einbauen kann, der Mal brav mit einer manipulate(...)-Methode aufgerufen wird. Und SomeClass soll eben auch außerhalb des Baumes verwendbar sein - dann aber ohne Filter und Abwandlungen und Extraprüfungen usw. Würde ich jetzt jede Art von Zusatz rausnehmen und extra einbauen wie beispielsweise extra std::function für Filter, extra std::function für Prüfungen des Rückgabewertes usw., dann hätte ich ziemlich viel Gedöns drin - was auch einfach kein Mensch braucht, der nur SomeClass nutzt. Daher der ganze Proxy.
Hilft das? Sorry für den ganzen Text...
Wenn's hilft, kann ich noch konkreter werden.
-
Na ja, wenn jetzt einfach nichts mehr kommt, löse ich es halt über das Interface. Interessiert mich nicht, ob irgendjemand dazu kommentiert, dass es "javalike" wäre. Dynamische Polymorphie löst man eben nicht über Templates. Und manchmal braucht man dynamische Polymorphie eben - auch in C++.
Jedenfalls komme ich nicht mit Lösungen weiter, die sich darauf fokussieren Interfaces und Polymorphie abzuschaffen statt mein Problem mit allen Anforderungen zu lösen...
Edit:
Den Rest habe ich Mal wegeditiert, hat leider den falschen Ton angenommen, Entschuldigung dafür. Danke an hustbaers nachfolgende Antwort.
-
Eisflamme schrieb:
Na ja, wenn jetzt einfach nichts mehr kommt, löse ich es halt über das Interface. Interessiert mich nicht, ob irgendein Praxisferner dazu kommentiert, dass es "javalike" wäre, es gibt einfach keine "c++-like" Implementierung, die meine (validen und total natürlichen) Anforderungen akzeptiert.
C++ ist eine multi-paradigm Sprache, und Runtime-Polymorphie über Basisklassen mit virtuellen Funktionen ist "perfectly C++-like".
Ein pöser Javaismus wäre alle solchen Klassen auf Krampf mit I zu prefixen und *nur* aus virtual-pure Funktionen bestehen zu lassen. Wenn eine Funktion nie überschrieben werden muss oder sogar darf, dann soll die auch nicht virtual sein. Ein weitere pöser Javaismus wäre einfach überall mit polymorphen Basisklassen draufzuhauen, auch wenn es in C++ viel elegantere Möglichkeiten dafür gibt. Nur so einen Fall hast du ja nicht. Es gibt keine elegantere Möglichkeit. Zumindest keine mir bekannte.
-
Okay, dankeschön. Wenn ich dasselbe schreibe, wird's ja geflissentlich ignoriert. Gut das Mal bestätigt zu hören.
-
ob irgendein Praxisferner dazu kommentiert
Ich programiere seit etwa 20 Jahren. Davon viele mit C++. Ich verdiene mein Geld mit C++.
Und jetzt du: Wie sehen deine Praxiserfahrungen mit C++ aus? Nein, nicht Java, PHP oder ...
Fuer mich wirkst du unerfahren, engstirnig und beratungsresistent.
die beendet jetzt vielleicht Mal diese Idiotie hier.
Du schreibst nicht mehr?
PS: Selten so einen kindischen Post gelesen, alle als Idioten zu bezeichnen, die Fassung verlieren und dann noch Hilfe erwarten.
-
hustbaer schrieb:
Ein pöser Javaismus wäre alle solchen Klassen auf Krampf mit I zu prefixen...
Javaismus ist nur, wenn das Interface auf -able endet. Sorry für Offtopic
-
knivil:
Erstmal sorry, ich wollte Dir nicht auf den Fuß treten. Mein Beitrag war in der Tat nicht gelungen. Du fühlst Dich wegen dem "javalike" angesprochen? Ich meinte damit nicht Dich, ich meinte eigentlich nur abstrakt gesprochen diejenigen, die mir Lösungen angeboten haben, die nicht zu meinem Problem passten, Hauptsache aber auf Interfaces verzichteten. Wenn das nach eigenem Ermessen keiner getan hat, dann braucht sich damit auch keiner angesprochen fühlen.Aber wenn Du so persönlich wirst, kommentiere ich zumindest Mal Deine Beiträge: Die Lösungen von Dir hatten in der Regel Lücken, haben nicht alles erfüllt, was ich wollte, und als ich das klarstellte, kam leider am Ende (erstmal schon) keine Antwort mehr. Ich habe stets versucht sie einzubauen und sie für meinen Anwendungsfall zu Ende gedacht (das ist engstirnig?). Es ging aber nicht! (traurigerweise anscheinend unwichtig) Und die Gründe dafür habe ich stets formuliert. Die Reaktion darauf war dann oft Empörung von Dir. Was soll ich da denn machen?
Zusammengefasst: Alle Vorschläge auszuprobieren, zu durchdenken und dann zu merken, dass es nicht passt und es deswegen abzulehnen ist für Dich also beratungsresistent? Ich spare mir jetzt Mal den Spruch, der das mit den 20 Jahren Erfahrung in Relation setzt.
Und
PS: Selten so einen kindischen Post gelesen, alle als Idioten zu bezeichnen
Das ist eine Unterstellung, das habe ich nicht getan (auch vor dem Edit nicht). Mit Idiotie meinte ich, dass alles, was nur nach Interface aussieht als Java-Style abgestempelt und grundsätzlich verpöhnt wird. Das heißt nicht, dass die Vertreter davon Idioten sind, nur das Konzept ist blanker Unsinn.
-
Okay, aber noch eine Frage:
Ein pöser Javaismus wäre alle solchen Klassen auf Krampf mit I zu prefixen und *nur* aus virtual-pure Funktionen bestehen zu lassen.
Vielleicht lese ich das falsch aber gilt der Teil nach dem "und" auch schon allein als pöser Javaismus? Denn was ist denn in dem Fall, dass eine Klasse oder Funktion ein Objekt erwartet, was eben nur durch die Schnittstelle definiert ist und wenn das eben zur Laufzeit festgelegt werden soll?
Denn das wäre bei mir ja eben auch der Fall mit dem Proxy und dann hätte mein Interface nur pure virtual Methoden.
-
Auch wennd as nachweislich nicht ankommen wird:
Ich habe eine Lösung ohne Interfaces gewählt, weil meine Erfahrung sagt, das "An der Stelle könnte ich A oder Wrapper<A> verwenden" keine zwingende Bedingung für late-binding ist. Ich glaube dir, das es jetzt aus deiner Perspektive und so wie du das designed hast als zwingend erforderlich erscheint, aber höchstwahrschinlich ginge es auch ohne irgendwelche Verrenkungen ohne komische Interfaces und const_cast und und und. Da du uns aber nur deine Situation und nicht dein Problem schilderst (darauf hatte ich dich ja schonmal hingewiesen) kriegst du halt nur die Pauschalaussagen.
-
Eisflamme schrieb:
Wir können der Einfachheit aber auch sagen, dass er protokolliert, während das SomeClass nicht macht - ansonsten werden die Aufrufe weitergeleitet.
Alter. Wegen solch einer Trivialität so ein Aufriss mit Patterngedöns und schwammigen Bezeichnungen/Buzzwords. Disorientierung pur dank OOP.
Wie werden wir diese Pest der Softwareentwicklung nur wieder los?
Gruß,
euer Observer aus der AbstractBeitragFactory
-
Ich hab bisher den Thread nicht wirklich verfolgt, ich hab quasi nur mitbekommen, dass du ein Objekt O hast, welches ein bestimmtes Interface I hat und du willst einen Proxy, der dein Objekt O hält, das Interface I erfüllt, aber noch ein paar Funktionalitäten (Logging) hinzufügt.
Wäre da nicht das Decorator Pattern was?
Sorry, wenn ich mit obigen Annahme falsch lag und das absolut nicht passt, dann ignoriert mich bitte.
-
Skym0sh0:
Decorator und Proxy unterscheiden sich ja nicht besonders voneinander. Proxy ist mit dem eigentlichen Objekt einfach härter verdrahtet. Aber ich finde die Unterscheidung nicht so wichtig, im Grunde genommen wäre es dasselbe.otze:
Immer, wenn ich anreiße, dass der Proxy als Stellvertreter für ein im Baum befindliches Objekt steht (was imo sehr konkret ist) und auch schildere, dass es sich um einen Baum von Mengen handelt, die in bestimmten Verhältnis zueinander stehen, kriege ich keine Antworten mehr. Ich habe schon häufig versucht das genaue Problem irgendwann später im Thread zu schildern, aber dann wird es zu lang und keiner liest mehr.Wie auch immer, danke für den Einwand. Ich muss eine ganze Menge umstrukturieren, wenn ich die Anforderung an das dynamische Setzen auflöse. Allerdings komme ich mit dem Interface auch nicht wirklich auf einen grünen Zweig.
Angenommen, ich nutze es statisch, ergibt sich dann aber immer noch das Problem, dass Proxy und eigentliches Objekt sich darin unterscheiden, dass ersterer kopierbar ist, weil er ja als Zeiger dient. Wie bringe ich das auf eine Ebene? Im Baum selbst befindlich sind ja die eigentlichen Objekte, nur eben der Zugriff auf diese geschieht über den Proxy. Wie würdest Du denn Proxy und eigentliche Klasse auf eine Ebene bringen, sodass die als Templateargument übergeben werden können und beide funktionieren?
Hm, man könnte natürlich für den Proxy operator-> überladen, dann könnte man einerseits EigentlicheKlasse* und Proxy übergeben und der Proxy würde sich verhalten wie ein Zeiger. Wäre das nicht was? Hmmm, das gefällt mir eigentlich echt gut.
Also etwa so:
class SomeClass { // ... }; class SomeClassProxy { public: SomeClassProxy(SomeClass& someClass) : someClass(&someClass) {} SomeClassProxy& operator->() {return *this;} void foo() {someClass->foo();} private: SomeClass* someClass; }; template<typename SomeClassReference> class SomeUIClass { private: SomeClassReference someClass; public: SomeUIClass(SomeClassReference someClass) : someClass(someClass) {} }; int main() { SomeClass a; SomeClassProxy b(a); typedef SomeUIClass<SomeClass*> Ui1; typedef SomeUIClass<SomeClassProxy> Ui2; Ui1 ui1(&a), ui2(b); }
Das entspricht auch brav meiner Logik, dass der Proxy eigentlich ja nur ein erweiterter Zeiger ist. Etwas blöd ist, dass man mit . genau so gut wie mit -> auf den Proxy zugreifen kann. Sollte man das über ne innere Klasse oder so noch absichern? Dann könnte man auch weitergehen und:
template<bool isConst> class SomeClassProxyBase { struct ProxyImpl { SomeClass* someClass; // oder const je nach isConst mit enable_if etc. ProxyImpl(SomeClass& someClass) : someClass(&someClass) {} // ibid. void foo() {someClass->foo();} void bar() const {someClass->bar();} } proxyImpl; // und wenn isConst true ist, kann man proxyImpl jetzt const machen public: SomeClassProxyBase(SomeClass& someClass) : proxyImpl(someClass) {} // oder const je isConst void SetSomeClass(SomeClass& someClass) {proxyImpl.someClass = &someClass;} // ibid. ProxyImpl& operator->() {return proxyImpl;} // oder wieder const }; // jetzt geht: typedef SomeClassProxyBase<true> ConstSomeClassProxy; typedef SomeClassProxyBase<false> SomeClassProxy; // und auch die vier Varianten const ConstSomeClassProxy proxy1; // Bezugsobjekt nicht austauschbar, nicht änderbar ConstSomeClassProxy proxy1; // Bezugsobjekt austauschbar, nicht änderbar const SomeClassProxy proxy1; // Bezugsobjekt nicht austauschbar, doch änderbar SomeClassProxy proxy1; // Bezugsobjekt austauschbar, und änderbar
Das fände ich chic, nein?
-
kannst du mich auf den Beitrag verlinken, an dem du dein Problem beschrieben hast?