Am logischsten konsistentesten Weg, Methoden der Form A zu erstellen, die A beeinflusst B?
https://softwareengineering.stackexchange.com/questions/17173
-
22-10-2019 - |
Frage
Ich habe ein gemeinsames Szenario, in dem ich ein Objekt habe (a), das ein anderes Objekt logischerweise beeinflusst (b), wie A eine Notiz zu B oder A als deaktiviert (normalerweise ist A ein Benutzerobjekt des Systems, und B ist einige Art von Geschäftsobjekt wie ein Kunde oder ein Termin).
In der Vergangenheit befand sich die Methode auf dem betroffenen Objekt wie:
customer.Disable(user); // user is marking the customer as disabled
... aber es liest nicht gut (es erscheint rückwärts). Das Gegenteil:
user.Disable(customer);
... liest besser, aber es gibt etwas, das ich nicht auf den Finger setzen kann, das einfach nicht richtig scheint. Die einzige andere Möglichkeit ist, einen statischen Vermittler zu haben:
ThirdPartyClass.DisableCustomer(customer, user);
... aber die Domänenobjekte werden am Ende mehr zu DTOs und ich bevorzuge den Domain -Modellansatz, weil es sinnvoller ist.
Die Frage ist also: Was macht am sinnvollsten? Gibt es eine Option, die ich nicht in Betracht gezogen habe? Wer besitzt tatsächlich eine Operation wie diese?
Bearbeiten
Mit der zweiten Option werden die umsetzbaren Objekte des Systems (normalerweise) (normalerweise) riesig, da die Benutzer des Systems diejenigen sind, die alles so gut wie alles tun und beeinflussen.
Lösung
Wie wäre es mit
customer.SetDisabledBy(user);
oder wenn Sie C# 4.0 oder eine andere Sprache mit ähnlichen Funktionen verwenden:
customer.SetDisabled(by: user);
Oder wenn Sie C# 3.5 oder neuer verwenden, können Sie Folgendes schreiben:
user.DisableCustomer(customer);
Während die Behinderungspflichtmethode eine Erweiterungsmethode in einer Klasse sitzt, die genannt wird CustomerActions
das sitzt zusammen mit dem Customer
Klasse. Auf diese Weise, wenn du bist using
das Customer
Namespace, User
wird diese Erweiterungsmethode haben. Wenn du nicht bist using
Es ist weg. User
ist kein Gott -Objekt mehr, aber die Vermittlerklasse ist nirgends im Code zu finden. Sie könnten auch eine haben OrderActions
Klasse, der bereitstellen wird user.CancelOrder(order)
, etc.
Gute Frage, habe mich zum Nachdenken gebracht!
Andere Tipps
user.Disable(customer)
Fast scheint es das zu sagen user
wird deaktiviert, seitdem user.Disable()
Würde so lesen, als würde es auf den Benutzer handeln, nicht auf den Kunden.
user.DisableCustomer(customer)
Lese ein bisschen besser, denke ich, da es klar macht, dass der Kunde behindert ist. Es bedeutet auch, dass Sie keine zusätzliche Klasse nur aus Gründen des Himmels benötigen.
Wenn Ihre Benutzerobjekte so viele Verantwortung übernehmen, dass es sich anfühlt, als hätten sie Methoden "Benutzer. Möglicherweise fühlen Sie sich besser, indem Sie entweder Schnittstellen verwenden, um die Rollen des Benutzers in domänenspezifische Stücke zu zerlegen, oder um Komposition zu verwenden, um dasselbe zu tun.
Schnittstellen
Sie haben eine IcustomerController -Oberfläche, die Benutzer implementiert. Wenn Sie die Benutzer benötigen, um Kunden zu deaktivieren, geben Sie den Kunden in ein icustomerControl und können ICC.DisableCustomer (Kunde) verwenden. Dies bietet eine natürliche Move -Lesart, da der Kunde nicht von einem Benutzer manipuliert wird, sondern von "etwas, das Kunden manipuliert".
Es lässt Sie auch flexibler, Dinge, die keine Benutzer sind, bei Bedarf keine Benutzer aktualisieren zu lassen.
Komposition
Nur eine Erweiterung der letzten Idee: Anstatt Icustomercontroller (IS-A) zu Benutzern, würden sie einen Icustomercontroller (HAS-A) besitzen. Jetzt liest der Anruf als user.customercontroller.disable (Kunde). Dies macht wiederum deutlich, dass Benutzer in einer bestimmten Eigenschaft (um Kunden zu betreffen) und nur gegen diese eine Domäne von Objekten (Kunden) zu handeln.
Sie hatten dieses Problem nicht erwähnt, aber eine Methode wie user.disable () könnte irgendetwas zu beeinflussen, selbst den Benutzer selbst. Einer der oben genannten korrigiert das.
Wenn Sie darauf hinweisen, dass viele Methoden in der Benutzerklasse haben Gott Objekt Anti-Muster. Es scheint, dass das Problem eine Kapselung ist, sodass der Datenfluss fließt
Der Benutzer führt eine Aktion auf einer Entität aus
kann neu interpretiert werden als
Der Befehl systemausführen auf Entität unter Bezugnahme auf den Benutzer
Dies würde ein Design bedeuten, das mit einem Befehlsmuster mit einem typischen Befehl wie folgt bevorzugt wird:
public class DisableCustomerCommand implements EntityCommand<Customer,User> {
// This is the single EntityCommand templated method
public Customer execute(Customer customer, User user) {
customer.setEnabled(false);
// TODO Add in audit code for User object
return customer;
}
}
Dies folgt dem allgemeinen OOD -Prinzip der Erstellung von körnigen einfachen Objekten, die sehr wenig tun, aber es gut machen.
Bearbeiten
Ich sollte darauf hinweisen, dass dies eine Überverfolgung ist. In der Regel müsste die Deaktivierung eines Kunden mehrere andere Felder erfordern, um zu ändern. Dies führt auch zu anämischen Einheiten (der Kunde wäre nur ein Haufen Getter/Setter), aber manchmal kann dies ein einfacheres Gesamtdesign sein.