2 Stimmen

Ist dieses Muster der Klassen- und Konstruktordefinition übermäßig redundant?

Ich stoße oft auf ein ähnliches Muster wie dieses:

class Person {
    public string firstName, lastName;
    public Person(string firstName, string lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
}

Das kommt mir übermäßig redundant vor (ich könnte mir vorstellen, dass es ausreicht, "Vorname" einmal statt dreimal einzugeben ), aber mir fällt keine geeignete Alternative ein. Hat jemand eine Idee? Vielleicht weiß ich einfach nichts über ein bestimmtes Entwurfsmuster, das ich hier verwenden sollte?

Edit - Ich glaube, ich muss das ein wenig ausführen. Ich frage nicht, wie man den Beispielcode "besser" machen kann, sondern eher "kürzer". In seinem jetzigen Zustand tauchen alle Mitgliedsnamen 4 Mal auf (Deklaration, Initialisierung, Konstruktorargumente), und das wirkt ziemlich redundant. Ich frage mich also, ob es ein Muster (oder semantischen Zucker) zu erhalten (ungefähr) das gleiche Verhalten, aber mit weniger Blähungen.
Ich entschuldige mich dafür, dass ich anfangs unklar war.

Bearbeiten - Dave's C# 3.0 Initialisierung Beispiel ist ziemlich nett, aber ich bin immer noch für eine allgemeinere Antwort hoffen :)

Bearbeiten - Ich weiß jetzt, dass bestimmte Sprachen weniger ausführliche Implementierungen zulassen; Java und C# möglicherweise nicht.

4voto

Dave D Punkte 7888

Es hängt davon ab, was Sie erreichen wollen und ob diese bestimmten Werte haben aus irgendeinem Grund bei der Erstellung des Objekts gesetzt werden muss.

Wenn Sie wollen, dass die gehaltenen Werte nur bei der Konstruktion gesetzt werden, müssen Sie die Klasse so deklarieren, wie Sie es getan haben:

class Person
{
    public string FirstName { get; private set; }
    public string Surname { get; private set; }

    public Person(string firstName, string surname)
    {
        // properties are read-only so must be set as part of the constructor
        this.FirstName = firstName;
        this.Surname = surname;
    }
}

Wenn es eine Anforderung gibt, dass die Eigenschaften bei der Erstellung festgelegt werden, dann müssen Sie auch die Klasse definieren, wie Sie es getan haben. Sie können jedoch auch die Möglichkeit vorsehen, diese Werte später zu ändern.

class Person
{
    public string FirstName { get; set; }
    public string Surname { get; set; }

    public Person(string firstName, string surname)
    {            
        this.FirstName = firstName;
        this.Surname = surname;
    }
}

Wenn die Eigenschaften jederzeit festgelegt werden können, d.h. es ist optional, ob sie bei der Erstellung festgelegt werden, dann brauchen Sie sie nicht als Teil des Konstruktors zu übergeben.

class Person
{
    public string FirstName { get; set; }
    public string Surname { get; set; }

    public Person()
    {
    }
}

In C# (3 und höher) können Sie diese Eigenschaften noch während der Erstellung festlegen, aber ich bin nicht sicher, welche Funktionen andere Sprachen haben, die dies widerspiegeln könnten:

var myPerson = new Person
{
    FirstName = "Test",
    Surname = "Test2",
};

Die Beispiele sind alle in C#, sollten aber für OOP im Allgemeinen gelten.

1voto

Don Roby Punkte 39870

Ich frage nicht, wie man den Beispielcode "besser", sondern eher "kürzer" machen kann. In seinem derzeitigen Zustand erscheinen alle Mitgliedsnamen viermal (Deklaration, Initialisierung, Konstruktorargumente), und das wirkt ziemlich redundant. Ich frage mich also, ob es ein Muster (oder semantischen Zucker) zu erhalten (ungefähr) das gleiche Verhalten, aber mit weniger Blähungen.

Soweit ich weiß, gibt es hier kein wirkliches Entwurfsmuster, aber wie viel von dieser Wiederholung notwendig ist, hängt stark von der Sprache ab.

In Scala könnte man dies zum Beispiel als Case-Klasse definieren und die ganze Wiederholung weglassen:

case class Person(firstName: String, lastName: String)

Auch in Ruby kann man eine Klassendefinition durch die Verwendung von Attribut-Accessors recht kurz gestalten:

class Person
  attr_accessor :first_name, :last_name
end

Keines von beiden entspricht genau dem, was Sie beschrieben haben, je nachdem, ob sie veränderbare Objekte oder Initialisierung definieren. Aber diese Art von Redundanz ist ganz eindeutig sprachabhängig.

1voto

Jörg W Mittag Punkte 349574

Das allgemeine Muster ist wahrscheinlich "eine gute Programmiersprache verwenden". Zum Beispiel ist diese eine Zeile von Scala ungefähr gleichwertig mit dem, was Sie oben geschrieben haben:

class Person(var firstName: String, var lastName: String)

Sie können einen Java-Decompiler über die generierten Person.class Datei, um zu sehen, was man in Java schreiben müsste, um die gleiche Funktionalität zu erhalten:

public class Person {
    private String firstName, lastName;

    public String getFirstName() {
        return this.firstName;
    }

    public void setFirstName(String paramString) {
        this.firstName = paramString;
    }

    public String getLastName() {
        return this.lastName;
    }

    public void setLastName(String paramString) {
        this.lastName = paramString;
    }

    public Person(String firstName, String lastName) {
        setFirstName(firstName);
        setLastName(lastName);
    }
}

Die Methodennamen werden etwas anders lauten (anstelle von getFirstName y setFirstName Scala verwendet nur firstName y firstName_= wobei letzteres wiederum kodiert wird als firstName_$eq (da es in Java illegal ist), aber die Absicht ist die gleiche.

Wenn Sie Ihre Klasse zu einem case class erhalten Sie auch automatisch die richtigen Implementierungen für equals y hashCode auch.

In Ruby würden Sie schreiben:

Person = Struct.new(:first_name, :last:name)

und erhalten wieder etwas, das dem Java-Beispiel ziemlich ähnlich ist. Struct.new tatsächlich eine richtige Klasse synthetisieren, die ungefähr so aussieht:

class Person
  attr_accessor :first_name, :last_name

  def initialize(first_name, last_name)
    self.first_name, self.last_name = first_name, last_name
  end
end

0voto

Waquo Punkte 830

Einige Sprachen sind so konzipiert, dass sie sehr ausführlich sind, und sie erlauben es in der Regel nicht, dies zu ändern.

Was Typendeklarationen angeht, so zwingt Java dazu, das Offensichtliche nicht nur anzugeben, sondern auch zu wiederholen. Die Typinferenz kann das größtenteils beheben, wenn man bereit ist, Sprachen wie Scala zu verwenden.

In Javascript, wo es weder statische Typen noch Klassen gibt, sind Konstruktoren nur Funktionen, die Objekte erzeugen, und es würde so aussehen:

function Person(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X