Frage

Ich möchte eine Frage stellen, wie Sie ein einfaches objektorientiertes Design-Problem nähern würden. Ich habe ein paar Ideen von meinem eigenen über das, was der beste Weg, um dieses Szenario anzusetzen, aber ich würde zu hören, einige Meinungen aus der Stack Overflow-Community interessiert. Links zu relevanten Online-Artikeln sind auch sehr geschätzt. Ich bin mit C #, aber die Frage ist nicht sprachspezifisch.

Angenommen, ich eine Video-Store-Anwendung schreibe dessen Datenbank hat eine Person Tabelle, mit PersonId, Name, DateOfBirth und Address Felder aus. Es hat auch eine Staff Tabelle, die einen Link zu einem PersonId hat, und eine Customer Tabelle, die auch zu PersonId verknüpft.

Eine einfache objektorientierte Ansatz wäre es, zu sagen, dass ein Customer Person „a“ und daher Klassen erstellen ein bisschen wie folgt aus:

class Person {
    public int PersonId { get; set; }
    public string Name { get; set; }
    public DateTime DateOfBirth { get; set; }
    public string Address { get; set; }
}

class Customer : Person {
    public int CustomerId { get; set; }
    public DateTime JoinedDate { get; set; }
}

class Staff : Person {
    public int StaffId { get; set; }
    public string JobTitle { get; set; }
}

Jetzt können wir eine Funktion sagen schreiben E-Mails an alle Kunden senden:

static void SendEmailToCustomers(IEnumerable<Person> everyone) { 
    foreach(Person p in everyone)
        if(p is Customer)
            SendEmail(p);
}

Dieses System funktioniert gut, bis wir jemanden haben, der sowohl ein Kunde und ein Mitglied des Personals ist. Unter der Annahme, dass wir die gleiche Person in zweimal haben, nicht wirklich unsere everyone Liste wollen, einmal als Customer und einmal als Staff, wir machen eine willkürliche Wahl zwischen:

class StaffCustomer : Customer { ...

und

class StaffCustomer : Staff { ...

Offensichtlich wird nur die erste dieser beiden würde die SendEmailToCustomers Funktion nicht brechen.

Also, was würden Sie tun?

  • Nehmen Sie die Person Klasse optional Verweise auf eine StaffDetails und CustomerDetails Klasse?
  • Erstellen Sie eine neue Klasse, die eine Person enthalten sind, sowie optional StaffDetails und CustomerDetails?
  • Erstellen Sie alles über eine Schnittstelle (z IPerson, IStaff, ICustomer) und drei Klassen erstellen, die die entsprechenden Schnittstellen implementiert?
  • Nehmen Sie einen völlig anderen Ansatz?
War es hilfreich?

Lösung

Mark: Das ist eine interessante Frage. Sie werden so viele Meinungen zu diesem Thema finden. Ich glaube nicht, gibt es eine ‚richtige‘ Antwort. Dies ist ein gutes Beispiel dafür, wo ein starres heirarchial Objektdesign wirklich Probleme verursachen kann, nachdem ein System aufgebaut wird.

Zum Beispiel können Sie sagen mit dem „Kunden“ gehen und „Personal“ Klassen. Sie stellen Ihr System und alles ist glücklich. Ein paar Wochen später, jemand darauf hinweist, dass sie beide ‚auf Personal‘ und ein ‚Kunde‘ sind, und sie sind nicht Kunden-E-Mails bekommen. In diesem Fall haben Sie eine Menge von Code-Änderungen (Re-Design, nicht erneut Faktor) zu machen.

Ich glaube, es wäre zu komplex und schwierig zu warten, wenn Sie eine Reihe von abgeleiteten Klassen haben, versuchen, alle Permutationen und Kombination von Menschen und ihren Rollen zu implementieren. Dies gilt insbesondere, da das obige Beispiel ist sehr einfach -. In den meisten realen Anwendungen, die Dinge komplexer sein

Für Ihr Beispiel hier, würde ich mit „Nehmen Sie einen völlig anderen Ansatz“. Ich würde die Person-Klasse, und dies in eine Sammlung von „Rollen“ implementieren. Jede Person kann eine oder mehrere Rollen haben wie „Kunde“, „Mitarbeiter“ und „Verkäufer“.

Dadurch wird es einfacher, Rollen hinzuzufügen, wie neue Anforderungen entdeckt werden. Zum Beispiel können Sie einfach eine Basis „Rolle“ Klasse, und neue Rollen aus ihnen abzuleiten.

Andere Tipps

können Sie möchten die Partei und Verantwortlichkeit Muster zu prüfen, mit

Auf diese Weise Person wird eine Sammlung von Verantwortlichkeiten hat, die vom Typ Kunden oder Mitarbeiter sein kann.

Das Modell wird auch einfacher sein, wenn Sie später mehr Beziehungstypen hinzufügen.

Der reine Ansatz wäre: Stellen Sie eine Schnittstelle alles. Als Implementierungsdetails können Sie optional eine beliebige der verschiedenen Formen der Zusammensetzung oder umsetzungs Vererbung. Da diese Implementierungsdetails sind, sind sie wichtig nicht zu Ihrem öffentlichen API, so sind Sie frei zu wählen, je nachdem, was Ihr Leben macht einfachste.

Eine Person ist ein Mensch, während ein Kunde nur eine Rolle ist, dass eine Person von Zeit zu Zeit übernehmen kann. Mann und Frau wären Kandidaten Person zu erben, aber Kunde ist ein anderes Konzept.

Das Liskov Substitutionsprinzip besagt, dass wir in der Lage sein müssen, abgeleiteten Klassen zu verwenden, wo wir Hinweise auf eine Basisklasse haben, ohne es zu wissen. Kunden vererben Person, die würde diese verletzen. Ein Kunde könnte vielleicht auch eine Rolle von einer Organisation gespielt werden.

Lassen Sie mich wissen, wenn ich Foredecker Antwort richtig verstanden. Hier ist mein Code (in Python, sorry, ich weiß nicht C # kennen). Der einzige Unterschied ist, ich etwas nicht mitteilen würde, wenn eine Person „ist ein Kunde“, ich würde es tun, wenn einer seiner Rolle „interessiert sich für“ das Ding. Ist das flexibel genug?

# --------- PERSON ----------------

class Person:
    def __init__(self, personId, name, dateOfBirth, address):
        self.personId = personId
        self.name = name
        self.dateOfBirth = dateOfBirth
        self.address = address
        self.roles = []

    def addRole(self, role):
        self.roles.append(role)

    def interestedIn(self, subject):
        for role in self.roles:
            if role.interestedIn(subject):
                return True
        return False

    def sendEmail(self, email):
        # send the email
        print "Sent email to", self.name

# --------- ROLE ----------------

NEW_DVDS = 1
NEW_SCHEDULE = 2

class Role:
    def __init__(self):
        self.interests = []

    def interestedIn(self, subject):
        return subject in self.interests

class CustomerRole(Role):
    def __init__(self, customerId, joinedDate):
        self.customerId = customerId
        self.joinedDate = joinedDate
        self.interests.append(NEW_DVDS)

class StaffRole(Role):
    def __init__(self, staffId, jobTitle):
        self.staffId = staffId
        self.jobTitle = jobTitle
        self.interests.append(NEW_SCHEDULE)

# --------- NOTIFY STUFF ----------------

def notifyNewDVDs(emailWithTitles):
    for person in persons:
        if person.interestedIn(NEW_DVDS):
            person.sendEmail(emailWithTitles)

Ich möchte vermeiden, das "ist" Check ( "instanceof" in Java). Eine Lösung ist die Verwendung eines Decorator-Muster . Sie könnten eine EmailablePerson erstellen, die Person schmückt, wo EmailablePerson Zusammensetzung eine private Instanz einer Person zu halten, verwendet und die Delegierten alle Nicht-E-Mail-Methoden, um die Person-Objekt.

Wir untersuchen dieses Problem auf dem College im letzten Jahr waren wir lernen eiffel, so dass wir die mehrfache Vererbung verwendet. Wie dem auch sei Foredecker Rollen Alternative scheint flexibel genug zu sein.

Was ist los eine E-Mail an einen Kunden zu schicken, die ein Mitarbeiter ist? Wenn er ein Kunde ist, dann kann er die E-Mail gesendet werden. Bin ich so im Denken falsch? Und warum sollten Sie „alle“ als E-Mail-Liste aufnehmen? Woudlnt es besser sein, eine Kundenliste zu haben, da wir mit „sendEmailToCustomer“ -Methode und nicht „sendEmailToEveryone“ Verfahren handelt? Auch wenn Sie die Liste „jeder“ nutzen wollen, können Sie nicht zulassen, dass Duplikate in dieser Liste.

Wenn keines dieser mit vielen redisgn erreichbar ist, werde ich mit der ersten Foredecker Antwort gehen und möglicherweise einige Rollen zu jeder Person zugewiesen haben sollte.

Ihre Klassen sind nur Datenstrukturen: keiner von ihnen jedes Verhalten hat, nur Getter und Setter. Vererbung ist unpassend hier.

Nehmen Sie einen völlig anderen Ansatz: Das Problem mit der Klasse StaffCustomer ist, dass Ihre Mitarbeiter könnten als nur Mitarbeiter beginnen und einen Kunden werden später auf, so dass Sie sie als Mitarbeiter müssten löschen und eine neue Instanz der StaffCustomer Klasse erstellen . Vielleicht eine einfache boolean innerhalb Staff Klasse von ‚isCustomer‘ würde unsere jede Liste erlauben (vermutlich davon ab, alle Kunden und alle Mitarbeiter aus entsprechenden Tabellen zusammengestellt) nicht die Mitarbeiter zu erhalten, wie es wird wissen, dass es bereits als Kunde aufgenommen wurde.

Hier einige Tipps: Aus der Kategorie „nicht einmal denken, dies zu tun“, hier sind einige schlechten Beispiele von Code auftreten:

Finder-Methode zurückgibt Object

Problem: Je nach Anzahl der Vorkommen fand die Finder-Methode eine Zahl gibt die Anzahl der Ereignisse darstellt - oder! Wenn nur eine gefunden kehrt das eigentliche Objekt.

Tu das nicht! Dies ist eines der schlimmsten Codierung Praktiken und es stellt Mehrdeutigkeit und vermasselt den Code in einer Weise, dass, wenn ein anderer Entwickler ins Spiel kommt sie oder er wird dich hassen, dies zu tun.

Lösung: Wenn es einen Bedarf für ein solche 2-Funktionalitäten. Zählen und das Abrufen eine Instanz erstelle 2 Methoden ein, die die Zählung und eine zurückgibt, die die Instanz zurückgibt, aber nie eine einzige Methode in beiden Richtungen zu tun

Problem: Eine abgeleitete schlechte Praxis ist, wenn eine finder Methode gibt entweder das eine einzelne Auftreten gefunden entweder ein Array von Vorkommnissen, wenn mehr als einer vorhanden. Dieser faule Programmierstil wird von den Programmierern alot getan, die die vorherigen im Allgemeinen tun.

Lösung: Mit diesem auf meinen Händen würde ich eine Reihe von Länge zurück 1 (eins), wenn nur ein Vorkommen gefunden wird und ein Array mit einer Länge von> 1, wenn mehr Vorkommen gefunden. Darüber hinaus überhaupt oder ein Array der Länge 0 in Abhängigkeit von der Anwendung zurückkehren würde null keine Vorkommen zu finden.

Programmierung an eine Schnittstelle und mit kovarianten Rückgabetypen

. Problem: Programmieren mit einer Schnittstelle und mit kovarianten Rückgabetypen und in den Telefonvorwahl Gießen

Lösung: Verwenden Sie stattdessen den gleichen Supertyp in der Schnittstelle definierte die Variable zu definieren, die auf den zurückgegebene Wert anweisen. Dies hält die Programmierung einer Schnittstelle Ansatz und Ihren Code sauber.

Klassen mit mehr als 1000 Zeilen sind eine lauernde Gefahr Verfahren mit mehr als 100 Zeilen sind zu einem lauernden Gefahr!

Problem: Einige Entwickler in einer Klasse / Methode zu viel Funktionalität stopfen, zu faul ist, um die Funktionalität zu brechen - dies führt zu geringer Kohäsion und vielleicht zu hohe Kopplung - die Umkehrung eines sehr wichtiges Prinzip in OOP! Lösung: Vermeiden Sie zu viel innere / verschachtelte Klassen - diese Klassen verwendet werden sollen, nur auf einer Pro-Bedarf-Basis, müssen Sie eine Gewohnheit, nicht tun sie mit! Mit ihnen könnte zu mehr Problemen führen wie Vererbung zu begrenzen. Lookout für die Code-Duplikat! Der gleiche oder zu ähnlichem Code konnte bereits in einiger Supertyp Implementierung existiert oder vielleicht in einer anderen Klasse. Wenn es in einer anderen Klasse ist, die nicht ein übergeordneter Typ ist verletzten Sie auch die Kohäsions Regel. Achten Sie auf statische Methoden aus - vielleicht haben Sie eine Utility-Klasse hinzufügen müssen!
Mehr bei: http://centraladvisor.com/it/oop-what -sind-the-best-Practices-in-oop

Sie wollen wahrscheinlich nicht Vererbung für diesen Einsatz. Versuchen Sie stattdessen:

class Person {
    public int PersonId { get; set; }
    public string Name { get; set; }
    public DateTime DateOfBirth { get; set; }
    public string Address { get; set; }
}

class Customer{
    public Person PersonInfo;
    public int CustomerId { get; set; }
    public DateTime JoinedDate { get; set; }
}

class Staff {
    public Person PersonInfo;
    public int StaffId { get; set; }
    public string JobTitle { get; set; }
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top