Laden...

Überladung von Konstruktoren mit quasi gleicher Signatur

Erstellt von bigeddie vor 7 Jahren Letzter Beitrag vor 7 Jahren 5.782 Views
B
bigeddie Themenstarter:in
372 Beiträge seit 2007
vor 7 Jahren
Überladung von Konstruktoren mit quasi gleicher Signatur

Hallo Gemeinde,

ich habe mal wieder eine Frage, deren Lösung vielleicht auch bei anderen so schon aufgetreten ist.

Ich möchte eine Klasse "blubber" ersten, welche 2 Konstruktoren mit quasi gleichen Signaturen aufweisen soll, wie


public class Blubber
{
  public Blubber(string foo, string bar)
  {
  }
  public Blubber(string flip, string flop)
  {
  }
}

was ja bekanntlich nicht funktioniert.
Die Erstellung eines eines neuen Types, welcher von string abgeleitet ist geht auch nicht, da string ja 'sealed' ist und der "Umweg" über 'using'


using flipstr = string;
using flopstr = string;
public class Blubber
{
  public Blubber(string foo, string bar)
  {
  }
  public Blubber(flipstr flip, flopstr flop)
  {
  }
}

schlägt ebenfalls fehl.
Gibt es dazu einen Workaround über welchen etwas derartiges darstellbar ist?

Viele Grüße

bigeddie

Man muß nichts wissen,
man muß nur wissen wer es wissen könnte
oder wo es steht😉

16.807 Beiträge seit 2008
vor 7 Jahren

Wozu soll das gut sein?
Sieht jetz für mich nach einem Designfehler aus.

J
251 Beiträge seit 2012
vor 7 Jahren

Wie wäre es mit nen Objekt/Klasse FooBar und FlipFlop mit jeweiligen Properties?

B
bigeddie Themenstarter:in
372 Beiträge seit 2007
vor 7 Jahren

Hallo Abt,

wieso Designfehler?
Je nach Aufgabenstellung will ich ja nicht 10 Überladungen eines Konstruktors produzieren, wenn zwei komplett andere Member bei instanziieren betroffen sind?

Hallo Jamikus,

die Eigenschaften sind untrennbar miteinander verbunden und ich möchte nicht 2 Klassen erstellen, welche das gleiche beschreiben repräsentieren.

Ein Gegenstand lässt sich z.B. durch sein Volumen und sein Gewicht, aber auch über sein Volumen und seine Dichte oder sein Gewicht und seine Dichte beschreiben.

Viele Grüße

Ernst Jürgen

Man muß nichts wissen,
man muß nur wissen wer es wissen könnte
oder wo es steht😉

T
314 Beiträge seit 2013
vor 7 Jahren

Nimm doch einfach einen Konstruktor mit 3 Parametern? 😃

16.807 Beiträge seit 2008
vor 7 Jahren

wenn zwei komplett andere Member bei instanziieren betroffen sind?

.. was sich jetzt für mich erstmal danach anhört, dass Du eine Klasse hast, die verschiedene Arten darstellen kann, was gegen ein gutes Design sprechen würde 😉
zB, dass Du ein "Fahrzeug" hast obwohl Du "Auto" und "Motorrad" darstellen willst.

Dahingehend ist es immer praktisch zu sagen, was Du denn wirklich tust und ob Du nicht evtl. mit einem anderen Design besser fährst.

Wenn es sich wirklich um das Beispiel des Gegenstands handelt, dann würde ich wohl - vermutlich - keinen Konstruktor sondern eine statische Methode verwenden.

Ding ding = Ding.FromDichte(xyz, abc);
742 Beiträge seit 2005
vor 7 Jahren

Dann mach doch einfach Factory Methods


class Blubber
{
     private Blubber(string foo, ..., sting flip, ... ) { }

     public static Blubber CreateFooBar(string foo, string bar)
     {
         return new Blubber(foo, bar, null, null);
     }
}

W
872 Beiträge seit 2005
vor 7 Jahren

Factory Methode finde ich am besten, weil man dann einen sprechenden Namen zum Erstellen des Objekts wählen kann.
Ansonsten bieten sich noch optionale Parameter beim Konstruktor an - das ist aber weniger sauber als die Factory Methode.
Allgemein empfehle ich für solche Fragen immer Framework Design Guidelines - dort gibt es die Regel:

CONSIDER using a static factory method instead of a constructor if the semantics of the desired operation do not map directly to the construction of a new instance, or if following the constructor design guidelines feels unnatural.

3.003 Beiträge seit 2006
vor 7 Jahren

Wenn die Kombination aus Parametern für eine Methode - egal ob Konstruktor oder nicht - nicht eindeutig ist, dann benutzt man einfach ein Objekt, das die möglichen Kombinationen von Paramtern kapselt.

FactoryMethod bewegt sich hart am Rand zum AntiPattern. Würde ich lassen.


public class Blubber
{
  public Blubber(BlubberIntializationObject initObject)
  {
        Init(initObject);
  }
}

class BlubbInitializationObject
{
   public string Foo { get; set; }
   public string Bar { get; set; }
   public string Foz { get; set; }
}

LaTino
EDIT: @weismat - in diesem Fall ist die beabsichtigte Operation aber direkt die Konstruktion. Kann man sicher auch anders sehen, aber ich stehe auf dem Standpunkt, dass diese Regel hier nicht zutrifft. Das Problem ist eine unsaubere Definition der Konstruktor-Signaturen.

"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)

B
bigeddie Themenstarter:in
372 Beiträge seit 2007
vor 7 Jahren

Hallo Abt, malignate, weimat und LaTino,

ziehen eure Vorschläge nicht einen erheblichen Aufwand innerhalb des Konstruktors nach sich?

@LaTino: ich habe einen ähnlichen, aber etwas detaillierteren, Ansatz angedacht.

Wäre es nicht alternative und ohne Einhaltung irgendwelcher Coding-Rules effektiver etwas in folgender Form umzusetzen:


public class Koerper
    {
        #region privates
        double volumen;
        double gewicht;
        double dichte;
        #endregion
        #region Constructors

        public Koerper()
        {
            
        }

        public Koerper(DichteUndVolumen dv)
        {
            dichte = dv.Dichte;
            volumen = dv.Volument;
        }

        public Koerper(GewichtUndVolumen gv)
        {
            gewicht = gv.Gewicht;
            volumen = gv.Volumen;
        }

        public Koerper(DichteUndGewicht dg)
        {
            dichte = dg.Dichte;
            gewicht = dg.Gewicht;
        }
        #endregion
    }

    public class DichteUndGewicht
    {
        public double Dichte { get; set; }
        public double Gewicht { get; set; }

        public DichteUndGewicht(double d, double g)
        {
            Dichte = d;
            Gewicht = g;
        }
    }

    public class GewichtUndVolumen
    {
        public double Gewicht { get; set; }
        public double Volumen { get; set; }

        public GewichtUndVolumen(double g, double v )
        {
            Gewicht = g;
            Volumen = v;
        }
    }

    public class DichteUndVolumen
    {
        public double Dichte { get; set; }
        public double Volument { get; set; }

        public DichteUndVolumen(double d, double v)
        {
            Dichte = d;
            Volument = v;
        }
    }

Nicht schön aber selten 😉

Viele Grüße

bigeddie

Man muß nichts wissen,
man muß nur wissen wer es wissen könnte
oder wo es steht😉

3.003 Beiträge seit 2006
vor 7 Jahren

Naja. Was ist hieran komplizierter?


public Koerper(KoerperInit initParams = null)
{
    if(initParams != null)
    {
        if(!initParams.IsValid) throw new ArgumentException("Neee, du.");

        _dichte = initParams.Dichte;
        _volumen = initParams.Volumen;
        _gewicht = initParams.Gewicht;
    }
}

class KoerperInit
{
     public double Dichte { get; set; }
     public double Volumen { get; set; }
     public double Gewicht { get; set; }

     public bool IsValid => Dichte > 0 && Volumen > 0 && Gewicht == 0 || Dichte > 0 && Gewicht > 0 && Volumen == 0 || Volumen > 0 && Gewicht > 0 && Dichte == 0;
}

Du willst, dass die Parameter ein bestimmtes Verhalten haben. Modellier das mit einer Klasse.

LaTino
EDIT: wenn man die ursprünglichen Parameter nicht mehr braucht, kann man auch direkt die Parameter als Membervariable der Klasse speichern. Nochmal etwas weniger Aufwand.

"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)

P
1.090 Beiträge seit 2011
vor 7 Jahren

Ich schließe mich da Abt an. Meines Erachtens ist es ein Designfehler.

Wie soll denn jemand der die Klasse benutzt wissen welchen Konstrukte er benutzen soll/muss ?
Intern wirst du dann auch immer Prüfen müssen welch Werte gesetzt wurden.
Die Klasse hat so einfach keine klare Zuständigkeit.

Wenn du bei Klassen Sachen hast die Ähnlich sind, aber nicht genau das gleiche bietet sich Vererbung an. Gleiches kommt in die Basis Klasse und was anders ist in die Abgeleitete Klasse. Dann weiß jeder der die Klasse instanziert genau was er bekommt und du weißt auch in der Klasse was alles zur Verfügung steht.

Sollte man mal gelesen haben:

Clean Code Developer
Entwurfsmuster
Anti-Pattern

3.170 Beiträge seit 2006
vor 7 Jahren

Hallo,

also ich würde hier definitiv einzelne Datentypen bauen die die unterschiedliche Semantik abbilden.
Das eine ist nun mal ein Gewicht, das andere ein Volumen, und nur weil beide numerisch gemessen werden, sind sie noch lange nciht dasselbe -> warum dann also dem Code alles als double unterjubeln?

Im folgenden Beispiel gibt es eigene Typen für Gewicht, Volumen und Dichte. Die sind dann vollständig flexibel kombinierbar im Konstruktor, und biten zudem die Möglichkeit, sehr leicht mit unterschiedlichen Einheiten umzugehen (ich habe das beispielhaft beim Volumen mal gemacht).

Solche Typen sind dann im Gegensatz zu zusammengesetzten Initialisierungsobjekten auch völlig universell einsetzbar.

public class Koerper
{
    #region privates
    Volumen volumen;
    Gewicht gewicht;
    Dichte dichte;
    #endregion
    #region Constructors

    public Koerper()
    {

    }

    public Koerper(Dichte d, Volumen v)
    {
        dichte = d;
        volumen = v;
    }

    public Koerper(Gewicht g, Volumen v)
    {
        gewicht = g;
        volumen = v;
    }

    public Koerper(Dichte d, Gewicht g)
    {
        dichte = d;
        gewicht = g;
    }
    #endregion
}

public struct Dichte
{
    double _value;
    public Dichte(double val)
    {
        _value = val;
    }
    // hier noch Factory- und Zugriffsmethoden (siehe Volumen)
}

public struct Gewicht
{
    double _value;
    public Gewicht(double val)
    {
        _value = val;
    }
    // hier noch Factory- und Zugriffsmethoden (siehe Volumen)
}

public struct Volumen
{
    double _value;
    public Volumen(double val)
    {
        _value = val;
    }

    // Factorymethoden um aus verschiedenen Einheiten zu erzeugen
    public Volumen FromKubikzentimeter(double val)
    {
        return new Volumen(val / 1000000);
    }


    public Volumen FromLiter(double val)
    {
        return new Volumen(val / 1000);
    }


    public Volumen FromKubikmeter(double val)
    {
        return new Volumen(val);
    }

    // Zugriff über verschiedene Einheiten
    public double Kubikzentimeter { get { return _value * 1000000; } }

    public double Liter { get { return _value * 1000; } }

    public double Kubikmeter { get { return _value; } }

}

Gruß, MarsStein

Edit: da war noch ein Fehler im Code...
und noch einer... die Factory-Methoden in Volumen hatten gar keine neuen Objekte erzeugt 😦 -> korrigiert

Non quia difficilia sunt, non audemus, sed quia non audemus, difficilia sunt! - Seneca

B
bigeddie Themenstarter:in
372 Beiträge seit 2007
vor 7 Jahren

Hallo,

also das mit der Überarbeitung für numerische Datentypen ist mir schon bekannt, wobei
@MarsStein: woher kommt der Type NumericField?

Wie verhält es sich jedoch sich jedoch mit 'sealed' Datentypen wie String (siehe Thread-Start) wäre dann nicht der Ansatz meines letzten Posts eine ebenso adequate Lösung?

Zum Thema "Designfehler" gibt es IMHO wahrscheinlich mehr als genug kommerziell vertriebene oder in irgendeiner Form publizierte Varianten.

Viele Grüße

bigeddie

@MarsStein: Sry zu spät mitbekommen, dass NumericField entfernt wurde.

Man muß nichts wissen,
man muß nur wissen wer es wissen könnte
oder wo es steht😉

3.170 Beiträge seit 2006
vor 7 Jahren

Hallo,

sorry, meine Edits haben sich da mit Deinem Post überschnitten.
NumericField habe ich rausgenommen (hatte zuerst eine Basisklasse gemacht, dachte dann aber, structs seien hier besser.
Zu sealed-Datentypen: Die structs von mir sind ja auch sealed... verhält sich genauso. Du baust ja eine Struktur, die den eigentlichen Wert wrappt, da ist es Dir egal ob sealed oder nicht.

Gruß, MarsStein

Non quia difficilia sunt, non audemus, sed quia non audemus, difficilia sunt! - Seneca

B
bigeddie Themenstarter:in
372 Beiträge seit 2007
vor 7 Jahren

Hallo MarsStein,

also dürfte es der beste Weg sein mit eigenen Parameter-Typen zu arbeiten, welche dann auch ihre eigenen Fehler habe können und eigene Unit-Tests haben sollten, oder liege ich da falsch?

Viele Grüße

bigeddie

Man muß nichts wissen,
man muß nur wissen wer es wissen könnte
oder wo es steht😉

3.170 Beiträge seit 2006
vor 7 Jahren

Hallo,

mit eigenen Parameter-Typen zu arbeiten, welche dann auch ihre eigenen Fehler habe können und eigene Unit-Tests haben sollten

Würde ich so sehen.
Wenn Du das Spielchen mit den Einheiten machen willst, gibt es im Prinzip auch was für Unit Tests.
Wenn Du das nicht brauchst (immer in den gleichen Einheiten rechnest), reichst Du ja nur einen Wert weiter, da würden sich UnitTests damm IMO nicht wirklich lohnen.

Ob das der "beste" Weg ist sei mal dahingestellt, das muss jeder für sich selbst entscheiden. Es ist aber auf jeden Fall das, was ich persönlich dafür halte 😉

Gruß, MarsStein

Non quia difficilia sunt, non audemus, sed quia non audemus, difficilia sunt! - Seneca

5.299 Beiträge seit 2008
vor 7 Jahren

Ich möchte eine Klasse "blubber" ersten, welche 2 Konstruktoren mit quasi gleichen Signaturen aufweisen soll Zunächstmal: "quasi gleiche Signaturen" gibt es nicht, entweder sind Signaturen gleich oder nicht.
Daraus folgt logisch: Mit klassischen Konstruktoren geht das nicht.

Dann sind statische Factory-Methoden sicherlich das nächst-einfache - die architektonischen Einwände dagegen hab ich nicht mal verstanden, aber ich bin eh bisserl Architektur-Anarcho, oder um eine ca. 2000 Jahre alten GuideLine anzuführen:
"Das Gesetz ist für den Menschen da, nicht der Mensch für das Gesetz".
😉

Da jetzt eigens neue Klassen zu erfinden, die dann auch einen Konstruktor haben, woraus sich dann Konstruktoren in Konstruktoren ergeben - und die ganze Klasse mit nur dem Zweck, dass konstruktiert werden kann - jo - findich etwas überdreht, und verstehe bigeddies anfängliche Bedenken wg. unverhältnismässigem Aufwand gut.
Nicht verstehe ich, was seine Bedenken zwischenzeitlich scheints aufgelöst hat. 🤔

Weil eigene Konstruktor-Konstruktor-Klassen zu erfinden ist numal aufwändig, und ohne mir direkt erschließlichem Nutzen, und ich denke, auch deren Benutzung wird vergleichsweise aufwändig und unintuitiv ausfallen.

Ich orientiere mich auch bischen an andere Factory-Methoden, wie Image.FromStream, Image.FromFile und sowas - völlig fehl-designed können die nicht sein - ich komm ja damit gut klar.

Der frühe Apfel fängt den Wurm.

3.170 Beiträge seit 2006
vor 7 Jahren

Hallo,

Da jetzt eigens neue Klassen zu erfinden, die dann auch einen Konstruktor haben, woraus sich dann Konstruktoren in Konstruktoren ergeben
...
Weil eigene Konstruktor-Konstruktor-Klassen zu erfinden ist numal aufwändig

das ist aber gar nicht das, worum es geht.
Bei meinem Ansatz z.B. ist das Ziel, Daten mit unterschiedlicher Semantik in unterschiedliche Typen zu packen. Gerade bei physikalischen Größen drängt sich mir persönlich diese Vorgehensweise geradezu auf.
Das Konzept ist auch nicht extra oder ausschließlich dafür da, verschiedene Konstruktoren bereitstellen zu können. Es steckt viel mehr dahinter, und das kann einem das Leben auch an vielen anderen Stellen erleichtern, da solche Typen eben wirklich universell einsetzbar sind.

Wenn man von Anfang an mit solchen semantischen Typen arbeitet, würde sich eine Frage nach den überladenen Konstruktoren in der Weise gar nicht stellen (weil z.B. ein Volumen eben keine Dichte ist).

Viele Grüße
MarsStein

Non quia difficilia sunt, non audemus, sed quia non audemus, difficilia sunt! - Seneca

3.003 Beiträge seit 2006
vor 7 Jahren

Noch ein Nachtrag, weil mir noch etwas einfiel, was völlig unter den Tisch gefallen ist: benannte Parameter.


class Koerper
{
    public double Volumen { get; }
    public double Dichte { get; }
    public double Gewicht { get; }

    public Koerper(double volumen = 0, double dichte = 0, double gewicht = 0)
    {
        Volumen = volumen;
        Dichte = dichte;
        Gewicht = gewicht;
    }
}

private static void Main()
{
    var koerper = new Koerper(gewicht: 2, volumen: 5);
}

Ist auf jeden Fall sehr gut lesbar, und macht dem Compiler auch keine Schwierigkeiten. Nachteil ist, dass dem nichtsahnenden Drittprogrammierer diese Art der Objekterzeugung evtl nicht in den Sinn kommt (und er auch kein IntelliSense hat).

Davon abgesehen hat MarsStein mMn Recht, wenn er den Schwerpunkt auf die Semantik der Parameter legt. Soweit zu gehen, für eine Einheit extra eine Klasse zu deklarieren, ist dann sicherlich Ermessenssache.

Letzten Endes kann der Compiler nicht zwischen zwei Methoden mit gleicher Signatur unterscheiden, weil er keinen Anhaltspunkt über die Bedeutung der Parameter hat. Also muss man diese Bedeutung irgendwie einführen - ob mit einem Parameterobjekt, oder mit extra-Klassen für jeden Parameter. (EDIT: oder, wie bei benannten Parametern, über den Namen, was ich für ziemlich furchtbar halte 😉 )

Dein (@ErfinderDesRades) Beispiel mit den Image-Methoden geht ein bisschen daneben, denn hier sind die statischen Methoden NICHT eingeführt worden, um dem Dilemma mit gleichen Signaturen zu entgehen, sondern um der gesamten Objektfamilie (Image ist abstrakt!) einheitliche Erzeugungen mitzugeben, ohne einen Konstruktor in der Basisklasse zu vereinbaren. Ganz anderes Spielfeld.

Egal. Mir fielen nur gerade die benannten Parameter wieder ein, das wollte ich noch nachtragen 😉.

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)

T
2.219 Beiträge seit 2008
vor 7 Jahren

Ich würde hier einen Konstruktor mit allen Parametern definieren.
Da die Typen aber alle identisch sind, müsste man dann eben die entsprechenden Parameter übergeben mit den entsprechenden Werten.

Lösungsmöglichkeiten wie Factory Methoden wären ein Ansatz, wenn Objekte der Klasse eben an vielen Stellen mit den entsprechenden Parametern angelegt werden müssten.
Wenn du aber nur 1-2 Stellen hast, dann kannst du die entsprechenden Konstruktor Aufrufe auch direkt im Code mit new machen.

Extra Datentypen halte ich auch für Weit über das Ziel hinaus, da diese dann auch nur jeweils eine Eigenschaft mit repräsentieren.
Viel zu viel Aufwand nur um eine Trennung der Konstruktor Parameter umzusetzen.

Lösungswege gibt es hier, wie so oft in der Programmierung, viele.
Wenn du aber den einfachsten Weg nutzen willst, dann nimm einen Konstruktor der eben drei Parameter nimmt und setze die entsprechende Parameter beim anlegen.

@LaTino
Der Ansatz mit den Bennanten Parametern ist schon ganz nett.
Wenn man aber unter anderen IDEs oder Platformen entwickelt, kann dies schon Probleme machen.
Trotzdem finde ich den Ansatz auch als gute Lösung, da man dann die entsprechenden Parameter + Werte direkt an den einen Konstruktor reichen kann was das Problem auch recht elegant löst.

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

3.170 Beiträge seit 2006
vor 7 Jahren

Hallo,

Extra Datentypen halte ich auch für Weit über das Ziel hinaus, da diese dann auch nur jeweils eine Eigenschaft mit repräsentieren.
Viel zu viel Aufwand nur um eine Trennung der Konstruktor Parameter umzusetzen.

Wenn es nur um die Konstruktorparameter geht - vielleicht.

Aber normalerweise macht man ja auch noch irgendwas anderes mit diesen Daten bzw. Typen. Man erstellt ja nicht aus Spaß an der Freud irgendwelche Objekte.
Und dann ist Typsicherheit doch sicherlich wünschenswert.

Wie ich bereits schrieb, haben solche Typen auch viele weitere Vorteile. Ich kann dann zum Beispiel auch Operatoren wie +/- definieren und so sicherstellen, dass ich wohl ein Gewicht zu einem Gewicht addieren kann, nicht aber versehentlich ein Gewicht zu einer Dichte - Typsicherheit eben, mit allem was dazugehört.
Mag auch daran liegen, dass ich beruflich sehr viel mit verschiedenen physikalischen Größen zu tun habe - bei uns ist das dann durchweg mit solchen semantischen Typen umgesetzt und es bringt uns viele Vorteile und macht den Code verständlicher.

Gruß, MarsStein

Non quia difficilia sunt, non audemus, sed quia non audemus, difficilia sunt! - Seneca

742 Beiträge seit 2005
vor 7 Jahren

Klar, wenn du Typen hast die du in deinem ganzen Modul oder sogar Anwendung verwenden willst, ist das eine super Sache. Beispielsweise Vectoren in einem Game (man könnte ja auch float[3] verwenden 😉

Aber um 200 Zeilen Code zu verbessern, sind 400 Zeilen für Datentypen (inkl. Equals, HashCode, operatoren, ToString usw. ) vielleicht ein bisschen unverhältnismäßig.

Keep it simple stupid

3.170 Beiträge seit 2006
vor 7 Jahren

Hallo,

400 Zeilen für Datentypen (inkl. Equals, HashCode, operatoren, ToString usw. )

Naja, implementierst Du halt das was Du brauchst.
Wenn man keine Operatoren oder Equals braucht, kann man's eben weglassen. Macht ja nicht in allen Fällen Sinn.
Ein paar ganz einfache Typen sind IMO schnell erstellt.

Um 200 Zeilen Code zu verbessern... naja - ich versuche solche Fragen eher allgemein zu betrachten. Wir wissen ja gar nicht, in welchem Zusammenhang der TE das braucht.

Meiner Erfahrung nach machen eigene Typen eigentlich immer Sinn, wenn man mit solchen Größen zu tun hat und das Programm nicht nur eben zum Lernen oder als Fingerübung dient.

Gruß, MarsStein

Non quia difficilia sunt, non audemus, sed quia non audemus, difficilia sunt! - Seneca

R
74 Beiträge seit 2006
vor 7 Jahren

richtig. einfach nur richtig, MarsStein

andererseits würde der Typ 'string' absolut ausreichend sein.

Der Einsatz von double würde nur zu Einschränkungen führen die nicht
wirklich gewollt sein können 😃

Das Volumen kann mit double doch nur einen Wert enthalten.

Viel flexibler wäre eine Angabe wie "50 ccm" oder "80 cubic Inch":)

Ohne semantischem Typ kannst dann rechnen "1 halb aufgeblasener
Luftballon + 100g Butter = X Meter. Lösung X ist 2".

In dem eingangs erwähnten Beispiel kann so etwas natürlich garantiert
nicht vorkommen. Aber nach dem 3ten Refactoring hast Du so eine
Rechnung im Programm und weißt es noch nicht einmal !

Oder permanente Unsicherheit und 100fachen Aufwand beim Testen.
Dann doch lieber 3 semantische Typen.

@malignate ein Programm von 200 Zeilen muss nicht verbessert werden.
Dieser Aufwand allein ist unverhältnismäßig.

Aber wenn die 200 Zeilen wo anders verwendet werden sollen dann ist
der "Aufwand" Pflicht. Wobei 400 Zeilen niemals zusammenkommen
bei 3 Typen mit je zwei Eigenschaften ...

16.807 Beiträge seit 2008
vor 7 Jahren

@malignate ein Programm von 200 Zeilen muss nicht verbessert werden.
Dieser Aufwand allein ist unverhältnismäßig.

Sagen wir, wir verbessern den Code in der Ausführungszeit um 50%.
Weil wir den Code aber Millionen Male aufrufen; verkürzen wir die Zeit von 20h auf 10h.
Das hochgerechnet auf 1 Jahr macht schon 3650h eingesparte Ausführungszeit.

Egal wie viel Quellcode es ist: niemals pauschal urteilen.
Eine noch so kleine Verbesserung kann sich massiv auswirken.

5.299 Beiträge seit 2008
vor 7 Jahren

Egal wie viel Quellcode es ist: niemals pauschal urteilen. Das ist selbst ein Pauschal-Urteil. 😉

Das ich auch pauschal bisserl einschränken würde. Weil es gibt durchaus identifizierbare Tendenzen.
Etwa die Tendenz, dass wenig Code leichter zu verstehen und zu warten ist als viel Code.

Das jetzt nicht als Pauschal-Urteil auffassen, sondern als Pauschal-Tendenz. Weil natürlich kann schlechter knapper Code auch unwartbarer sein als eine komplexere Klassenstruktur, die dafür aber sorgfältig und transparent konstruiert ist.

Ändert nichts an der Tendenz, ist ja auch nur eine Tendenz.

Der frühe Apfel fängt den Wurm.

16.807 Beiträge seit 2008
vor 7 Jahren

.. gut, wenn man es so auffasst und auslegt, dann werden wir uns in 3 Millionen Jahren noch im Kreis drehen.
Bringt also nichts, sowas.

5.299 Beiträge seit 2008
vor 7 Jahren

Versteh nicht, was du meinst.
Die Tendenz "Weniger Code ist besser" ist doch ein brauchbarer Kompass - da läuft man grad nicht im Kreis.

Klar gibts noch weitere Kompasse - du zB scheinst dich sehr an Ausführungsgeschwindigkeit zu orientieren.

Ist halt (die durchaus diskussionswürdige) Frage, wie man die verschiedenen Kompasse gewichtet, wenn sie in verschiedene Richtungen zeigen (was sie ja nicht immer tun).

(Ich sehe eher das problem, dass die Diskussion ziemlich OffTopic ist)

Der frühe Apfel fängt den Wurm.

P
1.090 Beiträge seit 2011
vor 7 Jahren

Die Tendenz "Weniger Code ist besser" ist doch ein brauchbarer Kompass - da läuft man grad nicht im Kreis.

Meines Erachtens ist es aus der falschen Richtung betrachtet. Es gibt zwar Prinzipien (z.B. DRY), die dazu Tendieren, das man weniger Code schreibt. Grundlegendes Ziel dahinter ist es aber nicht möglichst wenig Code zu haben, sondern bei DRY nur eine Stelle die die Verantwortlichkeit hat.

Wichtiger als wenig Code ist meines Erachtens, Lesbarer, Wartbare und Veränderbarer Code (grob auch in der Reihenfolge). Oder um noch weiter zu gehen. Im Enddefekt geht es um Wirtschaftlichkeit. Genialer Quellcode, denn keiner außer den Entwickler selber keiner Versteht, kann einfach Schwer gewartet werden. Und wenn der Entwickler im Urlaub ist oder anderweitig nicht zu erreichen. Ist das wirklich ein Problem[1]. Egal wie kurz er ist.

@ErfinderDesRades
Abts aussage nicht zu Pauschalieren, als Pauschalierung zu bezeichnen. Ist ja eigentlich auch schon wieder eine Pauschalierung. 😉

Und meines Erachtens hat er hier an dem Prunkt recht. Es gibt nicht einfach in der IT den einen Weg, sondern man muss den Kontext betrachten. Und im Zweifel heißt das auch mal gegen grundlegende Prinzipien der OOP zu verstoßen. Da sollte man aber wissen was man macht. Und die Prinzipien wirklich verstanden haben.

Sollte man mal gelesen haben:

Clean Code Developer
Entwurfsmuster
Anti-Pattern

5.299 Beiträge seit 2008
vor 7 Jahren

Ein anderes Prinzip, was ebenfalls zu weniger Code tendiert, ist KISS. Vermutlich gibts sogar noch weitere.
Das stört mich bischen, dass "kurzer Code" hier fast immer als Gegensatz zu Lesbar, Wartbar, Veränderbar, Wirtschaftlich hingestellt wird.
Das Gegenteil ist doch der Fall: Kurzer Code ist lesbar, wartbar, veränderbar, wirtschaftlich - zumindest tendenziell.
Daher kann man Kürze eine Art heuristischen Tendenz-Indikator ansehen, für den Grad der Umsetzung anderen genannten Punkte.

Auch widersprichst du mir nur scheinbar, tatsächlich aber wiederholst das von mir bereits gesagte:

natürlich kann schlechter knapper Code auch unwartbarer sein als eine komplexere Klassenstruktur, die dafür aber sorgfältig und transparent konstruiert ist.


Abts aussage nicht zu Pauschalieren, als Pauschalierung zu bezeichnen. Ist ja eigentlich auch schon wieder eine Pauschalierung. 😉 netter Versuch, aber es ist überhaupt keine Verallgemeinerung, wenn ich sage: "Diese - genau diese! - Aussage ist selbst eine Pauschalisierung".
Daher kanns auch keine Pauschalisierung sein.


Hat eiglich einer Interesse an einem konkreten Beispiel? Ich hab neulich was zu Permutationen verzapft, anhand einer recht komplexen Vorlage, und mein Machwerk ist deutlich kürzer, wartbarer, verständlicher ausgefallen.

Der frühe Apfel fängt den Wurm.

Hinweis von gfoidl vor 7 Jahren

Bitte bleibt beim Thema -- der Rest kann ggf. in einem separatem Thread diskutiert werden.