Laden...

Genereller weg Objekte "sicher" nach aussen zu geben.

Erstellt von Lexodus vor 17 Jahren Letzter Beitrag vor 17 Jahren 8.777 Views
L
Lexodus Themenstarter:in
254 Beiträge seit 2005
vor 17 Jahren
Genereller weg Objekte "sicher" nach aussen zu geben.

Hallo Leute

Ich wollte mal fragen obs in C#|.NET ne Möglichkeit gibt, Objekte nach aussen "readonly" zu markieren (und nicht mittels Implementation auf dem Objekt selbst).

Mit readonly mein ich, dass das Objekt nicht geändert aber abgefragt werden darf.

Gutes Beispiel, anhand einer Liste, wie man es nicht machen sollte;
http://msdn2.microsoft.com/de-de/library/e78dcd75(VS.80).aspx

Readonly auf der Implementation ist ja wohl ein schlechter Scherz? Würde zwar in die "Architektur" von .NET passen 😉

Einfach nen Clone zu machen und den zurückzugeben ist mir auch zu "unsauber".

Gruss

If you can't make it, fake it.

3.003 Beiträge seit 2006
vor 17 Jahren

Eine Eigenschaft, die nur den getter implementiert, wäre nach aussen readonly...oder versteh ich dich komplett falsch?

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

L
Lexodus Themenstarter:in
254 Beiträge seit 2005
vor 17 Jahren

@LaTino

Ja hast du mich, das Objekt sollte readonly sein.
Bei nem Get eines ReadOnly Props kannst du das zurückgegebene Objekt immernoch verändern.

read-only als modifizierer einer Variable ist auch nicht das was ich möchte...

@Edit:

Wenn es keinen Weg gibt, wie macht ihr das jeweils, wenn ihr Objekte vor Änderung schützen wollt?

If you can't make it, fake it.

L
Lexodus Themenstarter:in
254 Beiträge seit 2005
vor 17 Jahren

Sowas hier;

"n genereller weg"

http://java.sun.com/j2se/1.4.2/docs/api/java/util/Collections.html
Hier die "unmodifiableList", ist im Interface zu Collections zu finden.

Aufm Interface wäre es ja noch OK, aber auf der Implementation?

If you can't make it, fake it.

49.485 Beiträge seit 2005
vor 17 Jahren

Hallo Lexodus,

ein genereller Weg für nicht-sealed Klassen ist einen Readonly-Wrapper zu schreiben. Das ist allerdings ein händischer Weg und kein Automatismus.

herbivore

L
Lexodus Themenstarter:in
254 Beiträge seit 2005
vor 17 Jahren

OK danke Herbivore.

If you can't make it, fake it.

S
8.746 Beiträge seit 2005
vor 17 Jahren

Mal ehrlich: Über Reflection kannst du jeden wie auch immer gearteten Schutzversuch aushebeln. Gegen "böswillige" Veränderung ist kein Kraut gewachsen.

Und Veränderung "durch Versehen" taucht vermutlich auch nicht in den Top100 der häufig gemachten Fehler auf.

L
Lexodus Themenstarter:in
254 Beiträge seit 2005
vor 17 Jahren

Original von svenson
Mal ehrlich: Über Reflection kannst du jeden wie auch immer gearteten Schutzversuch aushebeln. Gegen "böswillige" Veränderung ist kein Kraut gewachsen.

Jop, du sagst es genau richtig, böswillig. Böswillig ist mir auch egal, ich will ja n sauberes API machen und wenn er wege geht die übers API nicht vorgesehen waren ist mir das egal... Gegen "böswillige" Veränderung ist kein Kraut gewachsen 😉

Grundsätzlich gilt für mich im API;
Alles was sichtbar ist, egal in welchem Kontext du dich gerade befindest, sollte setzbar sein. Wenn es da n Objekt oder n Property gibt das du "nicht setzen solltest" obwohl dus kannst, ist das API nicht so doll. (Also ich mein in Prinzip alles was "public" ist). Sowas sollte gekapselt sein.

Das hier;

Und Veränderung "durch Versehen" taucht vermutlich auch nicht in den Top100 der häufig gemachten Fehler auf.

Würde ich jetzt so nicht unbedingt unterschreiben, kommt drauf an in welchem Umfeld du programmierst.

Ich arbeite im Team an einer realtiv komplexen Software/Umgebung, für mich ist sauberes Design also absolut unabdingbar weil nicht jeder Entwickler "weiss" was ich "weiss" deshalb muss er mein API bedienen können ohne Angst zu haben das falsche Objekt zu manipulieren.

Wenn du vor nem "grossen" API hockst, bei dem sich der Entwickler eben genau nicht wirklich gedanken über so Zeug gemacht hat und du weisst nie richtig ob das was du machst immer funktioniert/keine Fehler schmeisst und auch das richtige Objekt manipuliert, dann viel Spass beim arbeiten...

Gruss

If you can't make it, fake it.

S
8.746 Beiträge seit 2005
vor 17 Jahren

Properties bieten doch genau das. Was eben nicht geht, ist den Zugriff per Aufrufkonvention zu regeln (a la C+-"const"). Ich persönlich finde das auch gut so, denn ein Objekte ist m.E. unabhängig vom Nutzungskontext. Wenn zwei Klassen den Zugriff auf Objekte einer dritten Klassen untereinander beschränken wollen, sollen sie das selbst tun. Dafür gibts Patterns. Dies in der Klasse selbst zu codieren würde bedeuten, konkrete Nutzungsaspekte in den Code zu ziehen. Das wiederspricht dem Wiederverwendungsgedanken.

Anders formuliert: Durch gutes Design kommt kein ernsthafter Bedarf nach so einem Zugriffsschutz auf. Typischweise will man sowas immer dann, wenn Objekte quer durch die Anwendung gereicht werden.

Ansonsten gibt es noch die .NET-Rechte. Damit könnte dein Code prüfen, ob der Aufrufer die nötigen Rechte besitzt. Ist natürlich auch nicht automatisch, aber extrem feingranular.

L
Lexodus Themenstarter:in
254 Beiträge seit 2005
vor 17 Jahren

Original von svenson
Anders formuliert: Durch gutes Design kommt kein ernsthafter Bedarf nach so einem Zugriffsschutz auf. Typischweise will man sowas immer dann, wenn Objekte quer durch die Anwendung gereicht werden.

Genau, das möchte ich irgendwie erreichen. Und ich weiss ganz ehrlich den "richtigen guten" Design-Weg nicht.
Ich beschreib gleich mal das was ich möchte (denn es ist ein sehr generelles Problem, das ich dann immerwieder gleich richtig lösen könnte).

Wie würdest du das machen Svenson? (Ich schaffs, ohne das mir der Aufwand explodiert, nur mit spezieller Namensgebung.)

Mein Fall;

Ich habe eine Klasse die heisst "FolderTree". Dieser Foldertree soll eine Struktur aus Ordnern darstellen (ziemlich ähnlich wie der Windows-DateiOrdner Baum).
Auf dieser Klasse FolderTree habe ich funktionen wie AddFolder RemoveFolder, GetFolder etc. so Standard-Zeug machen.

Dann gibts die Klasse "Folder" die den einzelnen Ordner darstellt. Diese Klasse hat ne Liste mit SubFolders, was wiederum "Folder" objekte sind. (Und die andern Props, wie der Namen der den Folder ausmacht, ID, etc.).

Dieser "FolderTree" und die "Folder" Klasse sind public (weil sie über die Remoting Schicht vom Service zum Client gereicht wird).

Die "FolderTree" Klasse soll als Helper oder so fungieren (damit man nicht ständig durch die einzelnen Folders per Rekursion zugreifen muss). Auf dieser Klasse kann ich auch Funktionen wie Folder-Count etc. abbilden (was ich auf dem einzelnen Folder-Objekt nicht kann).

Wenn ich also auf der Server-Seite die "Folder" in der SQL-DB ablegen will, so wäre es für mich ja nur von Interesse an "Alle Folder" vom "FolderTree" zu kommen ohne rekursiven Aufruf oder so, weil ich eigentlich die Folder nur Sequentiell abspeichern möchte.

Und genau dieser Aufruf zerbricht meinen Kopf: "FolderTree.AllFolders" den hätt ich so gern weil ich so sehr schön und effizient die Daten auf der DB abspeichern kann, genau dieses "property" verleitet aber unter umständen nen andern Entwickler daran zumzurschrauben. Wenn ich auf dieser ManagerKlasse, ne Funktion habe, die mir "alle Folders" ausgiebt so denke ich fühlt sich ein Entwickler versucht, direkt auf einem ListenElement dieser Liste Sachen hinzuzufügen (Folder.SubFolders.Add(Folder newFolder)).

Darum wollte ich soetwas wie ne "readonly" Sache, ich möchte das der Entwickler merkt, sollte er auf diese Art zugreifen, dass es ein Fehler ist.

Anscheinend ist das was ich mit "readonly" will auch garnicht so weiteres möglich, weil sich dann wieder die Frage nach den darin enthalteten Unterobjekten aufwirft.

Hast du Svenson, eine Idee was ich falsch mache? Wo ich schwächen habe oder was man anders bauen müsste das man nicht in dieses Problem rennt?
Wenn du sogar n Pattern im Kopf hast, wäre ich sehr froh darum.

Danke für die Hilfe

Gruss

@Edit: In dieser Beschriebenen, "meiner" Architektur habe ich keine Zwänge irgendwas so zu machen wie ich es hier beschreibe. Ich bin gerade daran, dies neu zu bauen. Wenn jemand also findet die Aufteilung von "FolderTree" und "Folder" ist schlecht, bitte posten, ich bin für jetwedrigen design-vorschlag offen.

Hab wieder Edits wie sau 😁

Ich persönlich finde das auch gut so, denn ein Objekte ist m.E. unabhängig vom Nutzungskontext.

Das finde ich auch, deshalb habe ich auch diese "Architektur" gewählt, weil das Folder-Objekt dann unabhängig davon ist für was es gerade verwendet wird. (Ne FolderStrukur kann ja an x-beliebigen Stellen zum Einsatz kommen)
Die FolderTree Klasse hat dann mehr infos über den eigentlichen Einsatzzweck).

If you can't make it, fake it.

S
8.746 Beiträge seit 2005
vor 17 Jahren

Hört sich irgendwie nach Visitor-Pattern an.

Eine andere Möglichkeit könnte darin liegen, die Folder-Collection nicht direkt, sondern über einen Indexer bereitzustellen. Hier kannst du ja einen Nur-Lese-Zugriff machen, d.h. man kann die Sub-Folder zwar auslesen, aber die Collection nicht modifizieren. Nachteil: Foreach geht nicht.

Die einfachste Lösung ist vermutlich das Verwenden eines Wrappers, der nur Read-Only-Zugriffe erlaubt. Ist zum Glück in .NET schon eingebaut und nennt sich treffenderweise ReadOnlyCollection<T>. Beispiel gibts in der Hilfe.

Dictionary wird damit zwar nicht abgedeckt, aber wie man ReadOnlyDictionaries baut sieht man hier ganz gut:

http://www.dotnetguru2.org/index.php?p=121&more=1&c=1&tb=1&pb=1

Ist zwar für 1.1 aber muss man nur für generics aufbohren. Allerdings sollte man noch IEnumerable implementieren, damit es auch mit Foreach klappt.

L
Lexodus Themenstarter:in
254 Beiträge seit 2005
vor 17 Jahren

Original von svenson
Hört sich irgendwie nach Visitor-Pattern an.

Eine andere Möglichkeit könnte darin liegen, die Folder-Collection nicht direkt, sondern über einen Indexer bereitzustellen.

Hmmm. Das mache ich schon? Er muss ja auf dem Folder objekt selbst Sachen auslesen, wie z.B. description?

Eine Read-Only collection kann man zwar nicht modifizieren, aber die darin Enthaltenen-Elemente und genau das ist das was mich stört, der Grund wieso ich hier poste.

Ein Dictionary gibt dir ne Read-Only ICollection auf .Values und .Keys, ist da schon "realtiv" direkt eingebaut, mein Problem besteht dann aber weiterhin...

.. So einfach ist es nicht 😉 ...

@Edit Danke denn noch für das Pattern, ich schaus mir gerade an

If you can't make it, fake it.

4.506 Beiträge seit 2004
vor 17 Jahren

Hallo Lexodus,

wie wäre eine Kopierfunktion in Deinen Gettern? Also überall wo Readonly-Zugriffe sein sollen eine Kopie des Dictionaries (oder ähnliches) machen, damit Änderungen daran nicht den internen Teil betreffen.

Ansonsten hilft Dir nicht viel. Du musst dann hergehen und die Daten in einer Form herausgeben, die eben nicht manipulierpar ist.

Gruß
Norman-Timo

A: “Wie ist denn das Wetter bei euch?”
B: “Caps Lock.”
A: “Hä?”
B: “Na ja, Shift ohne Ende!”

L
Lexodus Themenstarter:in
254 Beiträge seit 2005
vor 17 Jahren

Original von norman_timo
Hallo Lexodus,

wie wäre eine Kopierfunktion in Deinen Gettern? Also überall wo Readonly-Zugriffe sein sollen eine Kopie des Dictionaries (oder ähnliches) machen, damit Änderungen daran nicht den internen Teil betreffen.

Gruß
Norman-Timo

Jopp hab ich mir auch schon überlegt, wäre eigentlich ganz OK (wie du richtig sagst es macht mir intern nichts "kaputt"). Wenn sich das einer nicht bewusst ist und "direkt" auf der Methode Folder[] GetAllFoders() iteriert um zu lesen, macht er jedes mal ne Kopie das kann auch wieder fies sein.

folgendes wäre schlimm;


foreach(Folder folder in Foldertree.GetAllfolders())
{
     Console.WriteLine(folder.Caption);
}

Vielleicht gibts ja aber ne "offizielle" Lösung weil das was ich will eigentlich recht häufig vorkommt (übertragen auf andere Probleme).

If you can't make it, fake it.

3.003 Beiträge seit 2006
vor 17 Jahren

Factory Method wäre auch ein Weg. Lass dir, wenn nötig, Objekte zurückliefern, deren Properties modifizierbar sind, und ansonsten welche, deren Properties ein SET nicht erlauben, beide Kinder einer Klasse, von deren Typ deine Objekte sind.

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

L
Lexodus Themenstarter:in
254 Beiträge seit 2005
vor 17 Jahren

Original von LaTino
Factory Method wäre auch ein Weg. Lass dir, wenn nötig, Objekte zurückliefern, deren Properties modifizierbar sind, und ansonsten welche, deren Properties ein SET nicht erlauben, beide Kinder einer Klasse, von deren Typ deine Objekte sind.

LaTino

Wenn ich das richtige verstehe, würde ich auf der FolderKlasse interne Methoden machen, die das setzen der Properties erlauben/unterbinden (ich muss nicht einzeln properties editierbar machen sondern immer nur alle) und diese dann über meine "FolderTree" factory nach Aussen geben.

So stelle ich mir das vor;


public string Caption
{
     set
     {
         if(!ReadOnly)
         {
              caption = value;
         }
         else
        {
            throw new NotAllowedException();
         }

}

Tönt ganz gut ich glaube so kann ich es machen, um den Clone komm ich dann nicht herum aber ich finds doch ganz gut. Danke!

If you can't make it, fake it.

3.003 Beiträge seit 2006
vor 17 Jahren

So geht's auch, ist aber nicht so flexibel wie Factory.

(ich tipp' das hier nur im Browser, dh keine Pistole auf Richtigkeit/Vollständigkeit)


public Interface IMightBeWriteable 
{
  public string Caption { get; set; }
}

public class Writeable : IMightBeWriteable 
{
  private string _caption;
  public string Caption { get { return _caption; } set { _caption = value; } }
}
public class CertainlyNotWritable : IMightBeWritable
{
  private string _caption;
  public string Caption { get { return _caption; } set { throw new AccessDeniedException(); } }
}

Deine Objekte lieferst du dann wahlweise als Writable oder CertainlyNotWritable. Das haengt vom Aufruf durch das Containerobjekt ab. Dann ist es nur noch (IMightBeWritable)myObject.Caption, und dir ist in dem Moment egal, ob man nun schreiben darf, oder nicht. Letzten Endes ist das aber nix anderes als der Vorschlag in meiner ersten Antwort 😁

LaTino
EDIT: kurzer Nachtrag

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

L
Lexodus Themenstarter:in
254 Beiträge seit 2005
vor 17 Jahren

Diesen Code den du mir gepostet hast, würde mir aber den Aufwand ziemlich stark erhöhen, weil ich die Klasse "Folder" doppelt implementieren muss (einmal als Writeable und einmal als Readonly).

Das mit der Factory gefällt mir eignetlich mehr, auch wenn ich da n Clone machen muss.

Trotzdem danke

If you can't make it, fake it.

3.003 Beiträge seit 2006
vor 17 Jahren

Dein Aufwand ist sicherlich höher - einmal. Der deiner Nachfolger, wegen denen du die ganze Überlegung ja überhaupt erst angestellt hast, verringert sich dramatisch...von Wartbarkeit und Wiederverwendbarkeit mal zu schweigen.
Wie mein Mentor in meinen ersten Berufsjahren gesagt hätte...
Ich kann's dir nur empfehlen 🙂.

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

L
Lexodus Themenstarter:in
254 Beiträge seit 2005
vor 17 Jahren

Ne eben nicht nur einmal.

Sachen doppelt implementieren ist immer schlecht. Dann musst du die gleiche Änderungen gleich 2 mal machen, wartbarkeit wird sehr viel mühsamer.
(Weil du wissen musst das dein Code doppelt vorhanden ist und auch doppelt gewaret werden muss).

@Edit; wenn du doppelt implementieren willst würde ich nicht solche Comics rumschreiben von wegen gegen ein Interface programmieren und nicht gegen die Implementierung 😉.

Erinnert mich irgendwie an den Interface-Wahn;

"Wenns nicht klappt mach n Interface auch wenns nichts nützt und deinen Aufwand blos erhöht 😉"

If you can't make it, fake it.

3.003 Beiträge seit 2006
vor 17 Jahren

Es geht eben darum, dass du Änderungen nicht an den Implementierungen, i.e. an den instanzierten Klassen selber, vornimmst. Genau das ist ja verkehrt: wenn du bei Änderungen hingehst und jede einzelne deiner Klassen verändern musst, weil sich an allen dasselbe verändert hat, dann hast du ein ernsthaftes Designproblem.

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

L
Lexodus Themenstarter:in
254 Beiträge seit 2005
vor 17 Jahren

Original von LaTino
Es geht eben darum, dass du Änderungen nicht an den Implementierungen, i.e. an den instanzierten Klassen selber, vornimmst. Genau das ist ja verkehrt: wenn du bei Änderungen hingehst und jede einzelne deiner Klassen verändern musst, weil sich an allen dasselbe verändert hat, dann hast du ein ernsthaftes Designproblem.

LaTino

Hmm, weiss jetzt nicht genau was du meinst.

Mit ner Factory könnte ich es relativ bequem lösen (wenn ich dann nen Clone von meinen Objekten im Falle eines AllFolders aufruf nach aussen gebe).

Dann kann ich ne "Folder" instanz kopieren, "readonly" = "true" und sollte derjenige der die Liste verwendet Änderungen machen bekommt er ne exception. Er kann die Objekte in der Liste also nur über die Methoden meiner Factory manipulieren, und nicht direkt auf der "AllFolders" Liste (weil ich es dort auf readonly setze).

Wenn dus mit interfaces machst, hast du den genau gleichen Code 2 mal in deinem Projekt und das ist grundsätzlich falsch (wie das Comic das du gezeigt hast) das sollte man garnie machen. (Du müsstest im Fall eines Weiteren Properties sogar dein Interface anpassen und alle die es Implementieren, was auch ziemlich heftig wäre, zusätzlich zum doppelten genau gleichen Code was ich viel schlimmer finde).

Gruss und "Mahlzeit"

If you can't make it, fake it.

3.003 Beiträge seit 2006
vor 17 Jahren

Grob gesagt...es geht darum, dass du nur den Code deiner Containerklasse verändern musst. Lass die doch einen Schalter haben - wird er gesetzt, füllst du die Liste, die zurückgegeben wird, wahlweise mit Writeables/CertainlyNotWriteables. Die Ausgabe kann dann rekursiv einfach nur die Caption-Property des Interfaces aufrufen.

So. Und wenn deine Objekte, wovon ich mal ausgehe, auch noch andere Eigenschaften neben der Caption haben, die von der veränderten Ausgabe nicht betroffen sind, und beide Klassen Writable und CertainlyNotWritable sich diese Eigenschaften teilen, dann pack die bitte auch in eine eigene Klasse, von der dann abgeleitet wird oder die beide als Eigenschaft haben (je nachdem, ob containment oder inheritance sich anbietet). Ich sehe nicht, wo da irgendwas doppelt vorkommen muss.

Ich schlag's ja nur vor, weil du erwähnt hast, dass dir das Problem häufig begegnet, und dir auf die Weise sicher 'n Haufen Ärger ersparst.

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

L
Lexodus Themenstarter:in
254 Beiträge seit 2005
vor 17 Jahren

Wenn du n Interface hast das "Writeable" representiert und n Interface "not writeable" dann musst du bei beiden Implementieren der Interfaces den Code kopieren (weil du in beiden Implementierungen das gleiche machst).

Du würdest also hingehen und sagen "oh, auf meiner Klasse Folder brauch ich noch ein Property" Creator, wo der BenutzerNamen abgelegt wird der den Ordner erstellt hat.

Dann gehst du hin definierst mal dein Interface um (oh oh, ein Interface das sich so schnell und wahrscheinlich ändern kann sollte kein Interface sein).

Nun musst du diese Implementierung machen und musst aber in beiden Implementierung den gleichen Aufruf einpflanzen. (Die eigentliche Abhandlung in der Basisklasse musst du auch noch machen).

Du musst "zwei Implementierungen die eigentlich gleich sind" warten.

Für mich persönlich ist diese Art und Weise genau das Gegenteil von dem Comic der gepostet wurde.

Alles in allem hast du auch keinen Vorteil dies mit Interfaces zu machen, der Aufwand erhöht sich blos.

Denn wenn du in der Klasse "Folder" dieses Readonly flag einbaust (dass du dann mit der Factory setzt) und der von aussen das SetProperty aufruft, ist es für ihn "von aussen" Betrachtet genau gleich wie bei der Methode mit Interfaces.

Das üble daran ist auch, weil man das Interface nach Aussen gibt und derjenige der den Foldertree anwendet dieses ja kennen muss, würde er jede Interface Änderung in aller härte zu spüren bekommen.

In meinem Fall würde ich dann ein "public Interface IMightBeWriteable" erstellen.
Später kommt wie gesagt eine neue Anforderung an den Ordner (was sehr wahrscheinlich ist) das ich z.B. den Creator auch noch darin haben muss...

Jetzt müsste ich ein "public Interface IMightBeWriteable1" Interface definieren (weil dieses Interface die neue Ausprägung meines Folders darstellt) wenn ein anderer Entwickler dieses Interface verwendet muss er den ganzen Code anpassen (er erwartet ja immernoch IMightBeWriteable) sonst kompiliert es nicht.

Wenn ich einfach n weiters Property auf der Floderklasse einführe, kann er dieses Property verwenden (sollte er den Erstellenden Benutzer brauchen) oder halt eben nicht. Mit nem Interface hätte er diese "flexibilität" nicht, dort muss er es einfach anpassen, egal ob er dieses Property braucht oder nicht.

N Interface sollte das sein was es heisst, ne "schnittstelle", mit diese Methode programmiert man eigentlich "der Implementierung" hinterher.... und sogar noch 2mal.

Jetzt kommt vielleicht das Argument, man kann ja das Interface gleich lassen und ändern, dann hast du aber das gleiche Problem, dass das Interface der implementierung "nachrennt" und man vom Interface keinen wirklichen Vorteil hat.

"Think in the interface not in the implementation"

Gruss

If you can't make it, fake it.

3.003 Beiträge seit 2006
vor 17 Jahren

Hm, nein, du hast mich nicht verstanden. Oben steht, was ich an deiner Stelle tun würde.

So, wie du vorschlägst, das Interface zu nutzen, geht der Sinn natürlich flöten, das ist mir schon klar. Ich kann nur wiederholen: wenn du zwei Implementationen hast, die du gleichzeitig warten musst, und behauptest, das läge am Interface, liegst du falsch.

Wird an dieser Stelle auch schon wieder zu weit am eigentlichen Problem vorbei, von daher lass ich's 🙂.

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

L
Lexodus Themenstarter:in
254 Beiträge seit 2005
vor 17 Jahren

Hmm ich habe mich eigentlich auf den Vorschlag den du mit den Interaces gepostet hast bezogen?

"wenn du zwei Implementationen hast, die du gleichzeitig warten musst, und behauptest, das läge am Interface, liegst du falsch."
Vielleicht habe ich mich falsch ausgedrückt, dass man zwei Implentierungen warten muss die sich einem Interface widmen ist föllig normal und klar.

Das du in beiden Implementation genau das gleiche nachbaust (also den Code duplizierst) ist nicht gut.
Finde, wir reden nicht am Problem vorbei, wir evaluieren die einzelnen Möglichkeiten 😉

Wo ich jetzt nicht mehr mit komme;
Du sagst;

So, wie du vorschlägst, das Interface zu nutzen, geht der Sinn natürlich flöten,

Den Vorschlag dieses Problem mit Interfaces zu lösen kam ja von dir? --> Und ich habe dann erklärt das es in diesem Falle nicht das richtige ist? --> Was du ja auch bestätigt hast, in dem du sagst das der Sinn flöten geht.

Vielleicht brauchen wir auch mal neutrale Schiedsrichter 😉.

Zudem ist die Materie auch nicht so ohne, wie man an den verschiedenen Ansätzen in diesem Thread ja sehen kann.

Gruss

@Edit: zuviele Absätze...

If you can't make it, fake it.

S
8.746 Beiträge seit 2005
vor 17 Jahren

Letztlich werden alle Lösungen darauf hinauslaufen, dass erheblich mehr Code anfällt. Die Frage die man sich stellen sollte ist: Wo ist der ROI? Ich behaupte, der wird nie eintreten, es sei denn man hat es mit "Volldeppen" zu tun, die die API nutzen. Angesichts der Tatsache, dass so etwas im gesamten .NET-Framework nicht zu finden ist, wird man auch keine Erwartungshaltung befriedigen.

Ich würde den zusätzlichen Aufwand sparen und stattdessen in eine gute Doku investieren.

L
Lexodus Themenstarter:in
254 Beiträge seit 2005
vor 17 Jahren

Original von svenson
Letztlich werden alle Lösungen darauf hinauslaufen, dass erheblich mehr Code anfällt. Die Frage die man sich stellen sollte ist: Wo ist der ROI? Ich behaupte, der wird nie eintreten, es sei denn man hat es mit "Volldeppen" zu tun, die die API nutzen. Angesichts der Tatsache, dass so etwas im gesamten .NET-Framework nicht zu finden ist, wird man auch keine Erwartungshaltung befriedigen.

Ich würde den zusätzlichen Aufwand sparen und stattdessen in eine gute Doku investieren.

Jop das kann gut sein, dass ich mein API zu stark absichern will 🙂. Mir war es aber wichtig noch andere Meinungen zu hören. (Ich finde aber den Vorschlag von LaTino mittels "Factory" und kleinen Änderungen in der Klasse Folder selbst, mal abgesehen von der Interface diskussion überhaupt net schlecht... Ist auch vom Aufwand her vertretbar.)

Mein Entwicklerteam hat auch gemeint, wenn der Aufwand zu gross ist soll ich die Methode einfach Dokumentieren und dementsprechend benennen. Vielleicht mach ich es auch so. (Darum habe ich hier auch die Frage gethreaded).

Gruss und nochmals danke an Beide!

If you can't make it, fake it.

3.003 Beiträge seit 2006
vor 17 Jahren

Original von Lexodus
Wo ich jetzt nicht mehr mit komme;
Du sagst;

So, wie du vorschlägst, das Interface zu nutzen, geht der Sinn natürlich flöten,

Den Vorschlag dieses Problem mit Interfaces zu lösen kam ja von dir? --> Und ich habe dann erklärt das es in diesem Falle nicht das richtige ist? --> Was du ja auch bestätigt hast, in dem du sagst das der Sinn flöten geht.

Richtig zitiert, falsch interpretiert 😉.
So, wie du es vorhast, geht der Sinn flöten.
So, wie ich es machen würde, ist er noch da. Lies noch mal in Ruhe, was ich geschrieben habe...es unterscheidet sich teilweise ziemlich von dem, was du draus machen wolltest.

svenson hat natürlich recht, was den ROI angeht. Ich kam überhaupt nur auf den Vorschlag, als du meintest, sowas hättet ihr häufiger 🙂.

Ist aber definitiv mal 'ne schöne Übung zum Thema Patterns, die ich noch nicht gesehn hab 🙂.

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

S
1.047 Beiträge seit 2005
vor 17 Jahren

ausgehend von folgendem post

Original von LaTino
So geht's auch, ist aber nicht so flexibel wie Factory.

(ich tipp' das hier nur im Browser, dh keine Pistole auf Richtigkeit/Vollständigkeit)

  
public Interface IMightBeWriteable   
{  
  public string Caption { get; set; }  
}  
  
public class Writeable : IMightBeWriteable   
{  
  private string _caption;  
  public string Caption { get { return _caption; } set { _caption = value; } }  
}  
public class CertainlyNotWritable : IMightBeWritable  
{  
  private string _caption;  
  public string Caption { get { return _caption; } set { throw new AccessDeniedException(); } }  
}  
  

Deine Objekte lieferst du dann wahlweise als Writable oder CertainlyNotWritable. Das haengt vom Aufruf durch das Containerobjekt ab. Dann ist es nur noch (IMightBeWritable)myObject.Caption, und dir ist in dem Moment egal, ob man nun schreiben darf, oder nicht. Letzten Endes ist das aber nix anderes als der Vorschlag in meiner ersten Antwort 😁

LaTino
EDIT: kurzer Nachtrag

wird ja ersichtlich, das man bei interfaces 2 implementierungen gebraucht hätte... Writeable und CertainlyNotWritable
da beide klassen sich nur dadurch unterscheiden das gewisse properties beim set eine exception werfen, würde alles andere gleich sein
daher kam der einwurf mit doppelten aufwand, weil beide klassen ansonten identische properties und methoden etc. haben.

soviel mal als schiedsrichter 😉

3.003 Beiträge seit 2006
vor 17 Jahren

Und was macht man, wenn man zwei Klassen hat, die sich 'nen ganzen Haufen Code teilen?

Genau, kapseln 🙂.
Und das hab ich auch geschrieben.

LaTino
EDIT: Ich weiss, ich bin stur 😉.

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

S
1.047 Beiträge seit 2005
vor 17 Jahren

wie würde das dann bei dem hier vorliegenden fall aussehen?

3.003 Beiträge seit 2006
vor 17 Jahren

class Writable : IMightBeWritable, ParentClass
{
/* no comment */
}

Und so fort. Man könnte auch - hallo, Aufwand 😉 - ein Interface schreiben, das Eigenschaften beschreibt, die sich abhängig von der Klasse, die sie beinhaltet, verhalten.
Und das wäre dann wirklich nur noch einmal Aufwand, allerdings von der Art, dass man sich lieber hinsetzt, Doku schreibt und auf die Vernunft seiner Nachfolger hofft 😄.

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

S
1.047 Beiträge seit 2005
vor 17 Jahren

hm, ich finde dann aber dein design etwas fragwürdig...

denn was mir vor schwebte, um den aufwand einzugrenzen, wäre eine abstrakte oberklasse, die alle gemeinsamen implementationen enthält, und subklassen, die dann dieses readonly abdecken... denn genau das machst du ja jetzt auch nur wieder in gewissen sinne... in sofern brauch man keine interfaces...

3.003 Beiträge seit 2006
vor 17 Jahren

In deinem Vorschlag leitest du von einer Klasse ab und fügst Properties für bestimmte Zugriffe hinzu. In meinem mache ich das prinzipiell genauso, mit einem Unterschied: von aussen ist bei deinen Subklassen nicht klar, dass sie die Eigenschaft überhaupt besitzen, dh bei dir würde (ParentClass)Object.Property nicht funktionieren. und genau diesen Aufruf würde ich ja in der Containerklasse benutzen - dh die Containerklasse macht bei mir einen Aufruf des Interfaces, nicht der konkreten Klasse (das meinte ich mit dem "Comic"). (IMightBeWritable).Object.Property funktioniert naemlich, ohne dass der Aufrufende einen Schimmer haben muss, ob er die Property setzen darf oder nicht.

Hab ich den Unterschied irgendwie deutlich machen können? Hab' den Tag passenderweise mit Doku schreiben verbracht, so dass ich etwas Matsch im Kopf bin 😉.

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

S
1.047 Beiträge seit 2005
vor 17 Jahren

In deinem Vorschlag leitest du von einer Klasse ab und fügst Properties für bestimmte Zugriffe hinzu. In meinem mache ich das prinzipiell genauso, mit einem Unterschied: von aussen ist bei deinen Subklassen nicht klar, dass sie die Eigenschaft überhaupt besitzen, dh bei dir würde (ParentClass)Object.Property nicht funktionieren. und genau diesen Aufruf würde ich ja in der Containerklasse benutzen - dh die Containerklasse macht bei mir einen Aufruf des Interfaces, nicht der konkreten Klasse (das meinte ich mit dem "Comic"). (IMightBeWritable).Object.Property funktioniert naemlich, ohne dass der Aufrufende einen Schimmer haben muss, ob er die Property setzen darf oder nicht.

nein.in meinem vorschlag würde in der abstrakten oberklasse für das property lediglich der rumpf stehen, die implementierung müßte dann in der unterklasse erfolgen...

da ich jetzt nicht genau die syntax von c# in der richtung kenne nicht teeren und federn bitte, ich korrigierdsa morgen

abstract class AbstractObject {
    public abstract string Contact { get; set };
    public string Owner { get {return owner;} }
}

class WriteableChild : AbstractObject {
    public string Contact { get {...}; set {...} };
}

class ReadonlyChild : AbstractObject {
    public string Contact { get {...}; set {...}};
}

nun geht das hier durchaus


AbstractObject o = new ReadonlyChild();
o.Contact = "test"; //da soll dann die exception kommen

vorteil von deinen ist nun, das man im nachhinein noch properties hinzufügen kann, ohne die elternklasse zu verändern... und man kann sie schachteln
das entspricht ja einem pattern, weiß net ob es dsa visitor war 🤔

T
512 Beiträge seit 2006
vor 17 Jahren

Also ich bin nicht sicher, ob ich das richtig verstanden habe, aber wie wärs, wenn du über Remoting nur ein eingeschränktes Interface auslieferst?

D.h. du gibst dem Client per se nur ein Objekt, vom Typ eines Interfaces, was alle notwendigen Funktionen beinhaltet, nicht aber die eigentlich verbotenen Operationen. Wenn er an verbotene Operationen ran will, muss er also schon mutwillig nen Cast auf die echte Klasse machen.
Das fände ich jetzt ne ziemlich unaufwändige und für dein Problem wirksame Methode.

Die zweite Möglichkeit wäre mit CodeAccessSecurity zu arbeiten. Die strittigen Funktionen benötigen eine gewisse Permission. Und die lässt nur den Server rein und weist die Clients ab.
Nachteil daran, man sieht den Fehler erst zur Laufzeit, nicht zur Kompilierzeit. Der Vorteil, es ist wirklich sicher und kann nicht so leicht umgangen werden.
Macht die Sache aber auch komplexer, als es nötig ist, glaube ich.

e.f.q.

Aus Falschem folgt Beliebiges

3.003 Beiträge seit 2006
vor 17 Jahren

@sheitman

Na, ich finde jetzt sind wir schon sehr dicht beieinander. Denk, so würde es ganz gut funktionieren...ob nun Interface ohne oder abstrakte Klasse mit vorgegebenen Methodenruempfen...hm, ganz ehrlich: mit solchen Entscheidungen tu ich mich immer schwer, neige eher zu Interfaces, weil ich da bisschen freier in der Gestaltung zu sein glaube.

Wäre vielleicht mal ein Thema für einen eigenen Thread.

LaTino
EDIT: Interface, Klassen und Vererbung ? für den interessierten Leser

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

49.485 Beiträge seit 2005
vor 17 Jahren

Hallo zusammen,

um nochmal zum eigentlichen Problem zurück zu kommen:

Hallo Lexodus,

Alles was sichtbar ist, egal in welchem Kontext du dich gerade befindest, sollte setzbar sein.

Das ist eine hehres Ziel, aber fast nie durchsetzbar. Schon bei so einfachen Klassen muss ein Protokoll eingehalten werden (z.b. öffnen ... schreiben ... schließen; d.h. man darf nicht vor dem Öffnen oder nach dem Schließen schreiben; es gibt massig weitere Beispiele).

Darum wollte ich soetwas wie ne "readonly" Sache, ich möchte das der Entwickler merkt, sollte er auf diese Art zugreifen, dass es ein Fehler ist.

Für einige der Standard-Collections gibt es fertige Readonly-Wrapper bzw. eben ReadOnlyCollection<T>.

Eine Read-Only collection kann man zwar nicht modifizieren, aber die darin Enthaltenen-Elemente und genau das ist das was mich stört, der Grund wieso ich hier poste.

Hm, die Elemente der Collection wären doch Folder. Warum sollten die nicht geändert werden können sollen? Ich denke, hier wäre ein weitere ReadOnly-Wrapper für die Folder nicht nötig. Möglich wäre er natürlich.

Wenn jemand also findet die Aufteilung von "FolderTree" und "Folder" ist schlecht, bitte posten, ich bin für jetwedrigen design-vorschlag offen.

Eigentlich sollte es möglich und besser sein, nur die Klasse Folder zu erstellen. Alle Operationen, die bisher in FolderTree enthalten sind, kämen dann in Folder und bezögen sich jeweils auf alle Ordner des Teilbaums, dessen Wurzel der Folder ist. Die Funktionalität von FolderTree bekommt man, wenn man die Operationen für den absoluten Wurzel-Folder aufruft.

Hallo svenson,

Durch gutes Design kommt kein ernsthafter Bedarf nach so einem Zugriffsschutz auf.

ist es dann ein schlechtes Design von .NET, dass String immutable sind und es readonly-Collections wie SelectedListViewItemCollection gibt?

Ich finde im Gegenteil, dass sich der Bedarf nach Zugriffsschutz durch Properties automatisch ergibt, weil Getter Informationen über den Zustand des Objekts nach außen liefern. Würde über diese Information eine Änderung des Zustands des liefernden Objekts ermöglicht, wäre die Kapselung verletzt. Also muss dies verhindert werden. Zugriffsschutz ist eine legitime Möglichkeit, dass zu erreichen.

herbivore

L
Lexodus Themenstarter:in
254 Beiträge seit 2005
vor 17 Jahren

Wow

Tolle Diskussion echt!

@sheitman
Danke für das "Schiedsrichtern" du sprichst mir aus der Seele und wir reden vom gleichen 🙂

@herbivore
Du hast das Problem auf perfekte Art und Weise erkannt. Mit folgendem Satz;

Eigentlich sollte es möglich und besser sein, nur die Klasse Folder zu erstellen. Alle Operationen, die bisher in FolderTree enthalten sind, kämen dann in Folder und bezögen sich jeweils auf alle Ordner des Teilbaums, dessen Wurzel der Folder ist. Die Funktionalität von FolderTree bekommt man, wenn man die Operationen für den absoluten Wurzel-Folder aufruft.

Hut ab! (Ich hätte das nicht gekonnt)

Ich habe unabhängig/nebenbei meine "Architektur" mit einem anderen Entwickler (aus der JavaWelt besprochen). Ich habe hier etwas versucht "das man nicht so macht". Wie du gesagt hast.

Die Klasse Folder ist soweit "richtig" FolderTree hab ich falsch aufgebaut. Ich muss hier leider auf die Java Doku verweisen vielleicht gibts die Doku irgendwo im MSDN.

http://java.sun.com/products/jfc/tsc/articles/jtree/

Although the JTree architecture defines a TreeModel interface, it defines no operations that affect the structure of the tree. Looking over the methods defined in the interface, you can see a variety of accessor methods, one method for changing the data associated with a node, and none at all for adding nodes, removing them, or shifting their position in the tree.

Ich wollte die Struktur über das TreeModel verändern, das ist falsch.

Danke und Gruss!

Lexodus

@Edit; Ich habe meine Klasse "FolderTree" nun gelöscht so wie ich sie machen wollte.

If you can't make it, fake it.

S
8.746 Beiträge seit 2005
vor 17 Jahren

Original von herbivore

Durch gutes Design kommt kein ernsthafter Bedarf nach so einem Zugriffsschutz auf.
ist es dann ein schlechtes Design von .NET, dass String immutable sind und es readonly-Collections wie SelectedListViewItemCollection gibt?

Nein, grundsätzlich sind immutable Geschichten schon eine gute Sache, aber im Falle von Strings gibt es noch besondere Gründe:

  1. sie haben in der Sprache Quasi-Value-Charakter ( siehe Vergleichsoperatorimplementierung)
  2. durch immutable sind sie automatisch Thread-safem
  3. Verwendung als Keys in Hashtables (Variante zu 1)

Was ich mit schlechtem Design meinte war: Wenn das Design einer Anwendung schlecht ist, dann ist oft nicht ersichtlich, zu welchem Zweck Objekte übergeben werden. Dann ist die Gefahr höher, dass das Objekt fälschlicherweise manipuliert wird.

Immutable Objects reduzieren dieses Risiko, sind aber leider sehr aufwändig. Daher mit Bedacht einsetzen.

Die ReadOnly-Collection sind in meinen Augen auch so eine Spar-Version von immutable. Ich persönlich hab die Dinger aber noch nie eingesetzt muss ich gestehen.

L
Lexodus Themenstarter:in
254 Beiträge seit 2005
vor 17 Jahren

Svenson hat sich doch mit dem Satz

"Durch gutes Design kommt kein ernsthafter Bedarf nach so einem Zugriffsschutz auf."

auf mein Beispiel bezogen? Was absolut richtig war.

Für mich ist es ja eigentlich auch so, dass der Bedarf nach einer "readonly" Collection nur dadurch aufkam weil ich es schlecht designet habe. Ich bin auch froh dass ihr das erkannt habt.

In meinem Beispiel, darf ich über das Modell nichts verändern was die Struktur betrifft, also weder Objekte hinzufügen/ändern, noch die innern objekte manipulieren (was ich ich mit der readonly collection sowieso nicht hinbekommen hätte).

Ich persönlich finde solche "harten/generellen Aussagen" auch viel nützlicher als sowas mit nem totalen overkill an Interfaces oder sonstigen Implementierungen nach zu bauen.

If you can't make it, fake it.

S
8.746 Beiträge seit 2005
vor 17 Jahren

Hier noch ein guter Artikel über die Vorzüge von immutable Objects:

http://en.csharp-online.net/CSharp_Coding_Solutions%E2%80%94Immutable_Types_Are_Scalable_Types

Man bedenke z.B. das Code, der nur mit immutable Objects arbeitet extrem einfach automatisch parallelisierbar ist. Und das ist ein großes Thema für die Zukunft. Irgendwie müssen die Mehrkernsysteme ja ausgelastet werden....

P.s.: Auch die anderen Artikel der Reihe sind lesenswert.