it-swarm.com.de

Sollten Sie in solchen Fällen immer die minimal erforderlichen Daten an eine Funktion übergeben

Angenommen, ich habe eine Funktion IsAdmin, die prüft, ob ein Benutzer ein Administrator ist. Angenommen, die Administratorprüfung erfolgt durch Abgleichen von Benutzer-ID, Name und Kennwort mit einer Regel (nicht wichtig).

In meinem Kopf gibt es dann zwei mögliche Funktionssignaturen dafür:

public bool IsAdmin(User user);
public bool IsAdmin(int id, string name, string password);

Am häufigsten greife ich zur zweiten Art der Unterschrift und denke:

  • Die Funktionssignatur gibt dem Leser viel mehr Informationen
  • Die in der Funktion enthaltene Logik muss nichts über die Klasse User wissen
  • Dies führt normalerweise zu etwas weniger Code innerhalb der Funktion

Manchmal stelle ich diesen Ansatz jedoch in Frage und stelle auch fest, dass er irgendwann unhandlich wird. Wenn zum Beispiel eine Funktion zwischen zehn verschiedenen Objektfeldern einem resultierenden Bool zuordnen würde, würde ich offensichtlich das gesamte Objekt einsenden. Aber abgesehen von einem solchen krassen Beispiel sehe ich keinen Grund, das eigentliche Objekt weiterzugeben.

Ich würde mich über Argumente für beide Stile sowie über allgemeine Beobachtungen, die Sie anbieten könnten, freuen.

Ich programmiere sowohl in objektorientierten als auch in funktionalen Stilen, daher sollte die Frage in Bezug auf alle Redewendungen gesehen werden.

82
Anders Arpi

Ich persönlich bevorzuge die erste Methode von nur IsAdmin(User user)

Die Verwendung ist viel einfacher. Wenn sich Ihre Kriterien für IsAdmin zu einem späteren Zeitpunkt ändern (möglicherweise basierend auf Rollen oder isActive), müssen Sie Ihre Methodensignatur nicht überall neu schreiben.

Es ist wahrscheinlich auch sicherer, da Sie nicht bekannt geben, welche Eigenschaften bestimmen, ob ein Benutzer ein Administrator ist oder nicht, oder die Kennworteigenschaft überall weitergeben. Und syrion macht einen guten Punkt, was passiert, wenn Ihr id nicht mit dem name/password übereinstimmt?

Die Länge des Codes in einer Funktion sollte eigentlich keine Rolle spielen, vorausgesetzt, die Methode erledigt ihre Aufgabe, und ich hätte viel lieber einen kürzeren und einfacheren Anwendungscode als einen Hilfsmethodencode.

123
Rachel

Die erste Signatur ist überlegen, da sie die Kapselung benutzerbezogener Logik im User -Objekt selbst ermöglicht. Es ist nicht vorteilhaft, eine Logik für die Erstellung des Tupels "ID, Name, Passwort" zu haben, das über Ihre Codebasis verteilt ist. Darüber hinaus wird die Funktion isAdmin kompliziert: Was passiert, wenn jemand ein id übergibt, das beispielsweise nicht mit name übereinstimmt? Was ist, wenn Sie überprüfen möchten, ob ein bestimmter Benutzer ein Administrator aus einem Kontext ist, in dem Sie sollten nicht sein Kennwort kennen?

Darüber hinaus als Hinweis auf Ihren dritten Punkt zugunsten des Stils mit zusätzlichen Argumenten; es kann zu "weniger Code innerhalb der Funktion" führen, aber wohin geht diese Logik? Es kann nicht einfach verschwinden! Stattdessen wird es an jedem einzelnen Ort verteilt, an dem die Funktion aufgerufen wird. Um fünf Codezeilen an einem Ort zu speichern, haben Sie fünf Zeilen pro Nutzung der Funktion bezahlt!

82
asthasr

Das erste, aber nicht (nur) aus den Gründen, die andere angegeben haben.

public bool IsAdmin(User user);

Dies ist typsicher. Insbesondere da Benutzer ein Typ ist, den Sie selbst definiert haben, gibt es wenig oder keine Möglichkeit, Argumente zu transponieren oder zu vertauschen. Es funktioniert eindeutig für Benutzer und ist keine generische Funktion, die int und zwei Zeichenfolgen akzeptiert. Dies ist ein wichtiger Punkt bei der Verwendung einer typsicheren Sprache wie Java oder C #, wie Ihr Code aussieht. Wenn Sie nach einer bestimmten Sprache fragen, möchten Sie möglicherweise ein Tag hinzufügen für diese Sprache auf Ihre Frage.

public bool IsAdmin(int id, string name, string password);

Hier reichen alle int und zwei Strings aus. Was hindert Sie daran, den Namen und das Passwort zu übertragen? Die zweite ist mehr Arbeit zu verwenden und bietet mehr Möglichkeiten für Fehler.

Vermutlich erfordern beide Funktionen, dass user.getId (), user.getName () und user.getPassword () entweder innerhalb der Funktion (im ersten Fall) oder vor dem Aufruf der Funktion (im zweiten Fall) aufgerufen werden Das Ausmaß der Kopplung ist in beiden Fällen gleich. Da diese Funktion nur für Benutzer gültig ist, würde ich in Java alle Argumente entfernen und dies zu einer Instanzmethode für Benutzerobjekte machen:

user.isAdmin();

Da diese Funktion bereits eng mit den Benutzern verbunden ist, ist es sinnvoll, sie zu einem Teil dessen zu machen, was ein Benutzer ist.

P.S. Ich bin sicher, dass dies nur ein Beispiel ist, aber es sieht so aus, als würden Sie ein Passwort speichern. Sie sollten stattdessen nur einen kryptografisch sicheren Kennwort-Hash speichern. Während der Anmeldung sollte das angegebene Passwort auf die gleiche Weise gehasht und mit dem gespeicherten Hash verglichen werden. Das Senden von Passwörtern im Klartext sollte vermieden werden. Wenn Sie gegen diese Regel verstoßen und isAdmin (int Str Str) den Benutzernamen protokollieren sollte, könnte das Kennwort stattdessen protokolliert werden, wenn Sie den Namen und das Kennwort in Ihren Code übertragen. Dies führt zu einem Sicherheitsproblem (schreiben Sie keine Kennwörter in Protokolle ) und ist ein weiteres Argument für die Übergabe eines Objekts anstelle seiner Bestandteile (oder die Verwendung einer Klassenmethode).

65
GlenPeterson

Wenn Ihre bevorzugte Sprache Strukturen nicht nach Wert übergibt, dann (User user) Variante scheint besser. Was ist, wenn Sie eines Tages beschließen, den Benutzernamen zu löschen und nur die ID/das Passwort zu verwenden, um den Benutzer zu identifizieren?

Außerdem führt dieser Ansatz unweigerlich zu langen und überzogenen Methodenaufrufen oder Inkonsistenzen. Ist das Übergeben von 3 Argumenten in Ordnung? Wie wäre es mit 5? Oder 6? Warum daran denken, wenn das Übergeben eines Objekts in Bezug auf Ressourcen billig ist (wahrscheinlich sogar billiger als das Übergeben von 3 Argumenten)?

Und ich bin (irgendwie) anderer Meinung, dass der zweite Ansatz dem Leser mehr Informationen gibt - intuitiv fragt der Methodenaufruf "ob der Benutzer Administratorrechte hat", nicht "ob die angegebene Kombination aus ID, Name und Passwort Administratorrechte hat".

Wenn das Übergeben des User -Objekts nicht zum Kopieren einer großen Struktur führen würde, ist der erste Ansatz für mich sauberer und logischer.

6

Ich hätte wahrscheinlich beides. Die erste als externe API mit der Hoffnung auf zeitliche Stabilität. Die zweite als private Implementierung, die von der externen API aufgerufen wird.

Wenn ich zu einem späteren Zeitpunkt die Überprüfungsregel ändern muss, würde ich einfach eine neue private Funktion mit einer neuen Signatur schreiben, die von der externen aufgerufen wird.

Dies hat den Vorteil der Leichtigkeit, die innere Implementierung zu ändern. Es ist auch sehr üblich, dass Sie zu einem bestimmten Zeitpunkt möglicherweise beide Funktionen gleichzeitig benötigen und die eine oder andere aufrufen, abhängig von einer externen Kontextänderung (z. B. können alte Benutzer und neue Benutzer mit unterschiedlichen Feldern gleichzeitig vorhanden sein).

In Bezug auf den Titel der Frage geben Sie in beiden Fällen in gewisser Weise die minimal erforderlichen Informationen an. Ein Benutzer scheint die kleinste anwendungsbezogene Datenstruktur zu sein, die die Informationen enthält. Die drei Felder ID, Kennwort und Name scheinen die kleinsten tatsächlich benötigten Implementierungsdaten zu sein, aber dies sind keine Objekte auf Anwendungsebene.

Mit anderen Worten, wenn Sie mit einer Datenbank zu tun haben, ist ein Benutzer ein Datensatz, während ID, Kennwort und Anmeldung Felder in diesem Datensatz sind.

3
kriss

Es gibt einen negativen Punkt, um Klassen (Referenztypen) an Methoden zu senden, nämlich Sicherheitsanfälligkeit bei Massenzuweisung . Stellen Sie sich vor, ich habe anstelle der Methode IsAdmin(User user) die Methode UpdateUser(User user).

Wenn die Klasse User eine boolesche Eigenschaft namens IsAdmin hat und ich sie während der Ausführung meiner Methode nicht überprüfe, bin ich anfällig für Massenzuweisungen. GitHub wurde 2012 von genau dieser Signatur angegriffen.

3
Saeed Neamati

Ich würde diesen Ansatz wählen:

public bool IsAdmin(this IUser user) { /* ... */ }

public class User: IUser { /* ... */ }

public interface IUser
{
    string Username {get;}
    string Password {get;}
    IID Id {get;}
}
  • Wenn Sie den Schnittstellentyp als Parameter für diese Funktion verwenden, können Sie Benutzerobjekte übergeben, nachgebildete Benutzerobjekte zum Testen definieren und haben mehr Flexibilität bei der Verwendung und Wartung dieser Funktion.

  • Ich habe auch das Schlüsselwort this hinzugefügt, um die Funktion zu einer Erweiterungsmethode aller IUser-Klassen zu machen. Auf diese Weise können Sie Code OO benutzerfreundlicher schreiben und diese Funktion in Linq-Abfragen verwenden. Einfacher wäre es immer noch, diese Funktion in IUser und User zu definieren, aber ich nehme an, es gibt einen Grund, warum Sie beschlossen haben, diese Methode außerhalb dieser Klasse zu platzieren?

  • Ich verwende die benutzerdefinierte Schnittstelle IID anstelle von int, da Sie so die Eigenschaften Ihrer ID neu definieren können. z.B. sollten Sie jemals von int zu long, Guid oder etwas anderem wechseln müssen. Das ist wahrscheinlich übertrieben, aber es ist immer gut zu versuchen, die Dinge so flexibel zu gestalten, dass Sie nicht an frühe Entscheidungen gebunden sind.

  • Hinweis: Ihr IUser-Objekt kann sich in einem anderen Namespace und/oder einer anderen Assembly als die User-Klasse befinden. Sie haben daher die Möglichkeit, Ihren Benutzercode vollständig von Ihrem IsAdmin-Code zu trennen, wobei nur eine referenzierte Bibliothek gemeinsam genutzt wird. Genau das, was Sie tun, ist ein Aufruf, wie Sie vermuten, dass diese Elemente verwendet werden.

2
JohnLBevan

Haftungsausschluss :

Bei meiner Antwort werde ich mich auf das Stilproblem konzentrieren und vergessen, ob eine ID, ein Login und ein einfaches Passwort aus Sicherheitsgründen ein guter Satz von Parametern sind. Sie sollten in der Lage sein, meine Antwort auf alle grundlegenden Daten zu extrapolieren, die Sie verwenden, um die Berechtigungen des Benutzers herauszufinden ...

Ich betrachte user.isAdmin() auch als äquivalent zu IsAdmin(User user). Das ist eine Wahl, die Sie treffen müssen.

Antwort :

Meine Empfehlung gilt nur für beide Benutzer:

public bool IsAdmin(User user);

Oder verwenden Sie beide nach dem Vorbild von:

public bool IsAdmin(User user); // calls the private method below
private bool IsAdmin(int id, string name, string password);

Gründe für die öffentliche Methode :

Wenn Sie öffentliche Methoden schreiben, ist es normalerweise eine gute Idee, darüber nachzudenken, wie sie verwendet werden.

In diesem speziellen Fall besteht der Grund, warum Sie diese Methode normalerweise aufrufen, darin, herauszufinden, ob ein bestimmter Benutzer ein Administrator ist. Das ist eine Methode, die Sie brauchen. Sie möchten wirklich nicht, dass jeder Anrufer die richtigen Parameter zum Senden auswählen muss (und Fehler aufgrund von Codeduplizierungen riskieren).

Gründe für die private Methode :

Die private Methode, die nur den minimalen Satz erforderlicher Parameter empfängt, kann manchmal eine gute Sache sein. Manchmal nicht so sehr.

Eines der guten Dinge beim Aufteilen dieser Methoden ist, dass Sie die private Version von mehreren öffentlichen Methoden in derselben Klasse aufrufen können. Zum Beispiel, wenn Sie (aus welchem ​​Grund auch immer) eine etwas andere Logik für Localuser und RemoteUser haben möchten (vielleicht gibt es eine Einstellung zum Ein- und Ausschalten von Remote-Administratoren?):

public bool IsAdmin(Localuser user); // Just call private method
public bool IsAdmin(RemoteUser user); // Check if setting is on, then call private method
private bool IsAdmin(int id, string name, string password);

Wenn Sie aus irgendeinem Grund die private Methode öffentlich machen müssen, können Sie dies auch. Es ist so einfach wie das Ändern von private in public. Es scheint für diesen Fall kein so großer Vorteil zu sein, aber manchmal ist es das wirklich.

Wenn Sie Unit-Tests durchführen, können Sie auch viel mehr Atomtests durchführen. Es ist normalerweise sehr schön, kein vollständiges Benutzerobjekt erstellen zu müssen, um Tests mit dieser Methode auszuführen. Wenn Sie eine vollständige Abdeckung wünschen, können Sie beide Anrufe testen.

1
diegoreymendez
public bool IsAdmin(int id, string name, string password);

ist aus den aufgeführten Gründen schlecht. Das heißt nicht

public bool IsAdmin(User user);

ist automatisch gut. Insbesondere wenn der Benutzer ein Objekt mit folgenden Methoden ist: getOrders(), getFriends(), getAdresses(), ...

Sie können die Domain mit einem UserCredentials-Typ umgestalten, der nur ID, Name und Kennwort enthält, und diese anstelle aller nicht benötigten Benutzerdaten an IsAdmin übergeben.

0
tkruse