Test/ Mocken statischer Methoden

[quote=Marco13]Aber machen wir’s so: Ich gebe dir zwei .class-Dateien. Jede enthält eine Klasse. Beide implementieren “Comparable<List>”, für eine lexikographische Sortierung. Dafür schreibst du dann einen Unit-Test. Und dann sagst du mir, welche der beiden die “schlechte”, “unflexible” mit dem bösen static ist.[/quote]Das kann ich natürlich nicht.

Das static wird ja auch erst dann “böse” wenn Änderungen anstehen. Dann führen statische Methoden zu Aufwändigen, schwer wartbaren und (nicht zu letzt) aufwändig (nicht unbedingt schwer) zu testenden Lösungen.

Und bei Klassen, deren Quellcode ich nicht kenne ist mit deren innerer Aufbau dann auch ziemlich egal, weil ich da nicht wissen muss oder will, wie die intern funktionieren. Das ist ja genau der Kern der OOP.

bye
TT

Ich finde, dann geht es nicht mehr um die Frage, ob static Methods OK sind oder nicht, sondern WAS man mit welchem Ziel weg-Mocken sollte. Wenn es darum geht, dürfte es ja überhaupt keine nicht-Interface-fields mehr geben, und vor allem keine, die nicht durch DI injected werden. Die Argumentation dahinter ist mir nicht klar. Bestimmte Konzepte mögen ja schön und gut sein und ihre Anwendungsfelder haben, aber manchmal muss man doch auch mal ein ganz normales, objektorientiertes Programm schreiben. Ohne das direkt jemandem vorwerfen zu wollen: Immer wenn es eine “neue” Technik gibt, wird die von einigen Leuten “zu exzessiv” eingesetzt. So gab es bei Java mal die Vererbungshölle und Exception-Wildwuchs, bei C++ mal Extreme Template Magic, jetzt bei Java vielleicht teilweise die Stream, und … offenbar … zumindest scheint es so… Programme, wo DI für alles verwendet wird (egal, ob es sinnvoll ist, oder nicht…)

Die Dinge die Du da erwähnst sind ja auch schlimmer als die Pest (Du hast Lambdas vergessen). Aber ich sehe die Problematik bei DI nicht. Der Code ist einfacher zu schreiben, lesen und testen. Das Ergebnis ist durchgängig objektorientiert. Und es ist kein zusätzlicher Aufwand notwendig, da die Tests einfacher werden, wird sogar weniger Aufwand notwendig als ohne DI.

Ich finde DI + Unit Tests fühlt sich zusammen so natürlich an, das ich mich im nachhinein frage, wie ich die Zeit überlebt habe, als ich beim schreiben noch alle x Minuten die main ausgeführt habe.

Wo genau siehst Du das Problem?

Gruß
Fancy

Kein unmittelbares “Problem”. Nur frage ich mich, ob “DI für alles” wirklich so sinnvoll ist. Das ganze driftet jetzt halt schon relativ weit weg von der Frage, ob static Methods OK sind oder nicht. Um deswegen nochmal eine Lanze für static methods zu brechen, und vielleicht zu verdeutlichen, warum mir bestimmte Dinge … nun, “absurd” … oder zumindest suspekt erscheinen: Der letzte Vorschlag von TT aus #33 war der hier:

[QUOTE=Timothy_Truckle;131505]
Also wird die Methode eben nicht-statisch (was nicht notwendiger weise auch “public” bedeutet) und die anderen Klassen bekommen die Util-Klasse injiziert:class Utils { double sum(List<Double> list) { double sum = 0; for (int i=0; i<list.size(); i++) { sum += list.get(i); } return sum; } }

    private final Util util;
    DefaultSummaries(Util util){this.util = util;}
    
    @Override
    public double computeAverageValueOf(List<Double> values)
    {
        return utils.sum(values) / values.size();
    }
}```[/QUOTE]

Wäre das ganze jetzt immernoch OK, wenn ich es ändern würde zu
```class Utils
{
    double sum(List<Double> list)
    {
        return nowFinallyReallyComputeThatFrigginSum(list);
    }

    private static double nowFinallyReallyComputeThatFrigginSum(List<Double> list)
    {
        double sum = 0;
        for (int i=0; i<list.size(); i++)
        {
            sum += list.get(i);
        }
        return sum;
    }

}

?
Oder sollte da dann nochmal eine Mock/DI-Schicht drumgewickelt werden (für die man aber wieder die gleiche Frage stellen könnte)? Es ist für mich wirklich nicht nachvollziehbar, warum man da ein per DI injectetes Objekt einfügen soll, wenn NIEMAND das sieht, es KEINEN Nutzen bringt, und es NUR eine Funktion ausführt (nochmal: Im mathematischen Sinn, f(x) -> y).

Einerseits stimme ich bestimmten Aspekten zu: Auch wenn das Beispiel (wie immer) etwas künstlich wirkt, reicht es schon, um einen (tatsächlich sogar vergleichsweise realistischen) Fall zu verdeutlichen: Mit einem kleinen stream().parallel().reduce(...) könnte man z.B. diese Aufsummierung parallel machen wollen. Das ändert nichts an der Funktion (f(x)->y), der Schnittstelle oder den Vor/Nachbedingungen, sondern nur an der Implementierung. Aaaber… jetzt kommt vielleicht jemand auf den Trichter, dass man das “Utils” ja alles gar nicht braucht, und stattdessen direkt
return list.stream().collect(summarizingDouble(Double::doubleValue)).average().get();
schreiben kann. Damit wird das injizierte Utils-Objekt auf einmal überflüssig. Oder soll man das dann “aus Prinzip”, und “weil es sowieso schon da ist” (oder: “weil sich die existierenden Infrastrukturen darauf verlassen, dass da irgendwas mockbares rein-injiziert wird”) wieder in eine Utils-Methode auslagern? (Deren Schnittstelle sich durch solche Dinge ja auch ändern könnte…?)

Es ist für mich bisher kein fachliches Kriterium erkennbar (oder explizit benannt worden), aus dem man schließen könnte, wie man den “Kern” dieser Mocking-Matrjoschka identifiziert. Das anfänglich genannte “Kriterium” (dass die ~…“Wahrscheinlichkeit dafür hinreichend gering ist (wie bei Math#max)”) ist nicht schlüssig.

Deswegen vielleicht nochmal explizit die Nachfrage:

Ist jemand der Meinung, dass ALLE Fields von ALLEN Klassen IMMER und ÜBERALL NUR per DI gesetzt werden sollten?

(Zugegeben, ich stehe der “Magie”, die mit DI verbunden ist, in mancher Hinsicht etwas skeptisch gegenüber, aber stelle es nicht grundsätzlich in Frage - nur denke ich, dass da dann nicht unbedingt etwas besonders leicht wartbares oder nachvollziehbares rauskäme…)

[QUOTE=Marco13]Wäre das ganze jetzt immernoch OK, wenn ich es ändern würde zu

{
    double sum(List<Double> list)
    {
        return nowFinallyReallyComputeThatFrigginSum(list);
    }

    private static double nowFinallyReallyComputeThatFrigginSum(List<Double> list)
    {
        double sum = 0;
        for (int i=0; i<list.size(); i++)
        {
            sum += list.get(i);
        }
        return sum;
    }

}

?
Oder sollte da dann nochmal eine Mock/DI-Schicht drumgewickelt werden (für die man aber wieder die gleiche Frage stellen könnte)? Es ist für mich wirklich nicht nachvollziehbar, warum man da ein per DI injectetes Objekt einfügen soll, wenn NIEMAND das sieht, es KEINEN Nutzen bringt, und es NUR eine Funktion ausführt (nochmal: Im mathematischen Sinn, f(x) → y).[/quote]

Könnte man schon machen, aber wozu? Welchen Mehrwert versprichst Du Dir gegenüber der nicht statischen Variante? Machst Du es per DI, ist es homogen zum Rest.

Ich möchte das nicht allgemein beantworten, aber aus meiner subjektiven Sicht:

  • Abhängigkeiten zu meinen eigenen Klassen: Ja, immer. Ausnahmen sind höchstens performancekritische DTOs.

  • Abhängigkeiten zu anderen Libs: Ja, wenn möglich. Hoffentlich hat der Autor keine statischen Funktionen eingebaut. :wink:

  • Abhängigkeiten zur Java API: Kommt drauf an. Container wie Set, Map, List nicht. Wobei ich spontan aber auch nicht wüste was dagegen sprechen würde. Klassen die das Verhalten beeinflussen aber schon. Beispiel zu dem Beispiel von SlaterB (kein field, aber ähnliche Problematik):

Variante1:

import java.util.Calendar;
import java.util.GregorianCalendar;

import javax.inject.Inject;
import javax.inject.Singleton;


@Singleton
public final class UtilitiesImpl implements Utilities {


    @Inject
    private UtilitiesImpl() {

    }


    @Override
    public BigDecimal berechneBrutto(final BigDecimal netto) {

        final GregorianCalendar cal = new GregorianCalendar();

        if (cal.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY)
            return netto.multiply(new BigDecimal(1.07));

        else
            return netto.multiply(new BigDecimal(1.19));

    }


}```

Variante2:
```import java.math.BigDecimal;
import java.util.Calendar;
import java.util.GregorianCalendar;

import javax.inject.Inject;
import javax.inject.Provider;
import javax.inject.Singleton;


@Singleton
public final class UtilitiesImpl2 implements Utilities {

    private final Provider<GregorianCalendar> calendarProvider;


    @Inject
    private UtilitiesImpl2(final Provider<GregorianCalendar> calendarProvider) {

        this.calendarProvider = calendarProvider;

    }


    @Override
    public BigDecimal berechneBrutto(final BigDecimal netto) {

        final GregorianCalendar calendar = calendarProvider.get();

        if (calendar.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY)
            return netto.multiply(new BigDecimal(1.07));

        else
            return netto.multiply(new BigDecimal(1.19));

    }


}```

Variante 2 gefällt mir im Test und vom Ausdruck der Abhängigkeit des Verhaltens besser.

Aber auch das hat leider Grenzen. File z.B. lässt sich nicht mocken. Man kann eigentlich nur einen Layer drumwickeln und muss akzeptieren diesen Layer nur als Integrationstest testen zu können. 

[QUOTE=Marco13;131596]- nur denke ich, dass da dann nicht unbedingt etwas besonders leicht wartbares oder nachvollziehbares rauskäme...)[/QUOTE]

Guice ist imho entstanden weil Google seinen eigenen Code nicht mehr warten konnte. Und imho war bereits ~2008 jedes Google Java Projekt (Adwords, Youtube,..) auch ein Guice Projekt. 

Gruß
Fancy

[quote=Marco13]Der letzte Vorschlag von TT aus #33 war der hier:[code von TT]Wäre das ganze jetzt immernoch OK, wenn ich es ändern würde zu[code von @Marco13 mit statischer Methode][/quote]Ich sag’s halt noch mal: Ja, es wäre ok, weil es die interne, nach außen nicht sichtbare Logik dieses Objekts ist.

Solange die Methoden private bleiben kannst Du die gerne static machen, wenn dass nun mal Dein Programmierstil ist.

Problematisch wird es erst, wenn Du diese Funktionalität wo anders wiederverwenden willst, und das dann über „Veröffentlichung der statischen Methode“ tust.
Das hatte ich aber schon erläutert.

[HR][/HR]

[quote=Marco13;131596]Ist jemand der Meinung, dass ALLE Fields von ALLEN Klassen IMMER und ÜBERALL NUR per DI gesetzt werden sollten?[/quote]Auf eine so pauschale Frage kann man natürlich nur pauschal „Nein“ antworten.

Die oberste Einschränkung ist, dass wir nur von private final-Feldern reden.
Für diese private final-Felder sehe ich 3 Anwendungsfälle, die man unterscheiden muss:

[ol]
[li]Das „Zielobjekt“ ist ein DTO, also ein reiner Datencontainer ohne über „Not Null“-Checks hinausgehende Logik. Da macht DI gar keinen Sinn, statischen Methoden aber auch nicht.
[/li][HR][/HR]
[li]Das „Zielobjekt“ ist Teil der Geschäftslogik und das Feld ein primitiver Datentyp oder ein DTO. Auch hier ist DI (per Framework) nicht wirklich zielführend, aber im Prinzip trotzdem sinnvoll, weil die Erzeugung eines DTOs nicht Aufgabe der Geschäftslogik ist, zumindest nicht deren Konstruktors.
[/li]
Das schießt aber ausdrücklich nicht aus, dass diese Geschäftslogik in ihren anderen Methoden DTOs erzeugt und nach außen gibt.
[HR][/HR]
[li]Das „Zielobjekt“ ist Teil der Geschäftslogik und das Feld enthält ein „Service“-Objekt, das
[/li][/ol]

Ich könnte mir aber vorstellen, dass Du eigentlich was anderes wissen wolltest:

Ja, ich.

bye
TT

Wie schon gesagt ist das jetzt eigentlich abgedriftet, ausgehend von einer nicht nachvollziehbaren „static methods sind böse“-Polemik, zu einer Ausdifferenzierung von Fragen zu DI und Mocking, zu denen ich nicht viel sagen kann.

(Ja, ich oute mich als planloser, rückständiger Idiot: Ich habe noch nie ein Mocking-Framework oder DI verwendet. Ich bin mir (vielleicht gerade deshalb) über die Probleme im Klaren, irgendwelche konkreten Insantiierungen für Konstruktoraufrufe durchführen oder irgendwelche Beziehungen mit settern aufbauen zu müssen. Für die wenigen Stellen, an denen das bisher relevant war, hatte ich mir Guice sowieso schon auf meine Merkliste gelegt - aber nur das, weil es für das, was ich mache, sehr selten relevant ist…)

Ich finde jedenfalls nach wie vor, dass es viele gute Gründe für static methods gibt. Sowas wie die angedeutete statische „sum“-Methode kann man direkt aufrufen, oder in eine „.java“-Datei kopieren, compileren und ausführen. Das ist ein Building Block, eine Funktion, die wiederverwendbar, verständlich und strightforward ist.

Konkret:

Man könnte diese statische Funktion in eine (tendenziell package-private) Klasse auslagern, und dann auch an anderen Stellen aufrufen, wo man eine Liste aufsummieren muss. Sooo schwer ist das doch nicht?! Oder nochmal konkret: Sollte man DAS dann per

class Utils {
    @Inject UtilsSumImpl utilsSumImpl;
    ...
}
interface UtilsSumImpl { ... }
class DefaultUtilsSumImpl { ... nowFinallyReallyComputeThatFrigginSumButThisTimeReally... }

hinter einer weiteren DI-Schicht verstecken? Es bringt nichst. Man könnte das beliebig tief schachteln, aber irgendwo müssen diese 3 Zeilen code stehen, die die Liste aufsummieren, und die gleichen Zeilen sollten möglichst nicht 10 mal an verschiedenen Stellen stehen.

Einige der als Alternativen genannten Vorschläge erscheinen mir wie FizzBuzzEnterprise, mit dutzenden Abhängigkeiten, die Infrastrukturen erfordern, der einer Wiederverwendung und Nachhaltigkeit nicht zuträglich sind, und (und das betone ich auch gerne 100 mal) keinen erkennbaren Vorteil bringen. (Nebenbei: Ich bin sicher, dass vor 10 Jahren auch Leute Vererbungshierarchien der Tiefe 20 verteitigt haben mit Aussagen wie „Sun macht das bei Java3D genauso“). Die Argumente zu DTOs, Geschäftslogik usw. mögen ja alle zutreffen - aber passen tun sie nicht. Es geht um die Implementierung einer interface-Methode in einer Klasse. DAS ist die public API. Da wird ein Vertrag umgesetzt. Und ob der nun mit einer static method umgesetzt wird, oder mit einem Injizierten Utils-Objekt mit Injiziertem DoubleAccumulatorProvider und den damit verbundenen Interfaces+Klassen und einer Dependency zu „SODIF“ (Some Obscure Dependency Injection Framework) sieht man von aussen nicht - und niemand kann mir erzählen, dass eine Umstellung des ganzen auf Kahan-Summation „einfacher“ wäre, wenn man dafür nicht

// return Utils.sum(list); // Auskommentieren
return SomeLib.kahanSum(list); // Einfügen

sondern irgendwelche Bindings aktualisieren und neue Interface-Implementierungen anbieten müßte…

… was? Mit verschiedenen Implementierungen existieren kann? Zustandsbehaftet ist? (Oder irgendwas anderes, was nichts mit „static“ zu tun hat? :smiley: )

Sorry Marco, aber für mich ist hier Schluss.

Deine “guten Gründe” für statische Methoden stehen auf der selben Stufe wie die Gründe für “20 Generationen Vererbungstiefe”: Beides war früher durchaus üblich, hat Probleme, wenn sich der Code weiter entwickelt.

Du willst das nicht sehen und die Vor-/Nachteile an “statischer Codeanalyse” fest machen. Das geht natürlich nicht.

Also Danke für die Diskussion.

bye
TT

Ja, ist OK, ich glaube auch, dass das an sich zu nichts führt. (Außer dazu, dass ich vielleicht mal versuche, Guice zu verwenden, auch wenn es keinen konkreten Anlass gibt ;-)). Die Weiterentwicklung (im Sinne von Nachhaltigkeit) ist mir eigentlich schon wichtig. Und ich weiß, dass Aussagen wie “Die Leute haben 30 Jahre OOP gemacht, ohne DI” nicht zielführend sind und einen falschen Eindruck vermitteln könnten. Und trotz der langen Diskussion glaube ich immernoch, dass ein Teil des “Meinungsunterschieds” von unterschiedlichen Sichtweisen herrührt, die (schwammig: ) davon abhängen, was man so den ganzen Tag programmiert und wo man bestimmte Techniken mit welchem Ziel einsetzt. Und ich glaube immernoch, dass eine Aussage zu der “Wahrscheinlichkeit, dass sich etwas ändert” (bzgl. Math#max) kein Argument (im Sinne eines fachlichen, objektiven Entscheidungskreiteriums) darstellt.

Aber was ich mit Sicherheit sagen (und wo mir auch niemand widersprechen kann) ist, dass man eine statische Methode “trivial” (d.h. mit einer einzigen Zeile!) in ein Objekt einwickeln und damit als nicht-statische Methode eines (injizierbaren, mockbaren) Objektes überall anbieten kann. Der umgekehrte Weg, von einem Objekt mit etlichen Annotations und Dependencies und Injizierten Unterobjekten, hin zu einer Funktion (für die das Objekt da ja eigentlich nur ein “Handle” ist), die man “mal schnell (notfalls per Copy&Paste) auch woanders” verwenden kann, kann deutlich schwieriger sein.

Dir Guice anzusehen ist imho schon eine gute Idee. Allerdings weiß ich nicht ob das alleine reicht. Im Zusammenhang mit DI liest man zwar immer vom injizieren alternativer Implementierungen, imho ist das aber ein Witz. Wenn ich von meinen eigenen Klassen ausgehe, gibt es für 99,99% der Klassen genau zwei Fälle:

  • Zur Laufzeit injiziert das DI Framework eine Instanz der EINZIG existierenden Klasse.
  • Im Test injiziert das Mock Framework einen Mock.

DI hat zwar auch sonst Vorteile, aber das volle Potential zeigt sich erst im Zusammenhang mit Tests. Viele scheinen Guice + mockito + Junit zu verwenden. Ich bevorzuge net.nanojl:injector + JMockit + TestNG, aber jede Kombination daraus sollte eigentlich funktionieren.

DI selbst ist dabei eigentlich Kinderkram. Das wichtigste dürfte schon in #15 und #45 auftauchen. Fehlen eigentlich nur die Module und wenn man mag automatische Factories. Der Rest sind Randbereiche oder Dinge die man lieber sein lässt (z.B. field injection).

Vermutlich sieht man die Vorteile auch erst wenn man mal zwei kleinere Projekte konsequent damit umgesetzt hat. Zwei, weil das erste in die Hose geht, beim zweiten kommt dann (vielleicht) der AHA! Moment. :wink:

Gruß
Fancy

Im Zusammenhang mit diesem Thread bin ich über einen Link gestolpert. (Ich dachte, das wäre von hier aus verlinkt worden, aber vermutlich war es doch von Stackoverflow - hatte mal allgemeiner nach dem Thema gesucht). Dieser Link war OOP Alternative to Utility Classes - Yegor Bugayenko . Wirklich überzeugt hatte mich das nicht. Eher erinnerte mich das an einen Fall, wo aus der Aussage „Wenn man nur einen Hammer hat, sieht jedes Problem wie ein Nagel aus“ geschlossen wurde, dass man einen Nagel lieber mit einem Schraubenschlüssel einhämmern sollte, um nicht Gefahr zu laufen, sich dem Vorwurf dieser Aussage ausgesetzt zu sehen.
Wie auch immer, aus Neugier hatte ich dann mal weitergesucht, was der Autor sonst noch so macht. Unter anderem dieses Projekt: GitHub - yegor256/takes: True Object-Oriented Java Web Framework without NULLs, Static Methods, Annotations, and Mutable Objects Wo in der readme steht:

  1. not a single null (why NULL is bad?)
  2. not a single public static method (why they are bad?)
  3. not a single mutable class (why they are bad?)
  4. not a single instanceof keyword, type casting, or reflection (why?)

(mit jeweils verlinkten Blog-Einträgen mit den Erklärungen).

[ot]
Man könnte für jeden der Punkte einen eigenen Thread aufmachen, aber kurz:

  1. Man sollte abwägen. Zu versuchen, grundsätzlich auf null zu verzichten, ist IMHO albern. Dazu ist es ZU sehr Teil der Sprache (schon bei „map.get(unknownKey)“ u.v.a.). Da auf Biegen und Brechen Optionals drumzuwickeln macht auch keinen Sinn, aber um zu sehen, wie er sie ansonsten vermeidet, müßte ich aber den Code noch genauer lesen.

  2. Dazu gleich :wink:

  3. Ja, minimize Mutability, schon OK, aber die Fälle, wo das absolut möglich ist, sind IMHO Ausnahmen

  4. Klar
    [/ot]

Zum eigentlichen Punkt:

  1. not a single public static method (why they are bad?)

Da steckt etwas drin, worauf ich ja mehrfach rumgehackt hatte: public !!!. Er verwendet „private static“ auch recht oft. Ist auch gut so, IMHO. (Und, wenn ich das richtig verstanden habe, ist damit der kontroverseste Punkt dieses Threads ja auch abgefrühstückt).

[ot]
(Eigentlich noch „on topic“, aber …)

Ich finde, dass auch „public static“ Methods OK sein können. In einer meiner libraries sind z.B. viele („alle“) Funktionen „public static“: DoubleTuples. Das war eine bewußte Entscheidung: Die Funktionen sind dort „kanonisch“ implementiert. Ich weiß, das ist nicht OO, es macht in mancher Hinsicht (!) keinen Sinn, und es gibt vieles, was dafür spricht, diese Methoden als Instanzmethoden in das DoubleTuple interface zu ziehen, und die kanonischen Implementierungen in die Abtrakte Basisklasse zu packen. Aber das habe ich nicht gemacht, weil ich die Funktionen, wie auch mehrfach erwähnt, als „building blocks“ ansehe, und die Option, die ich eher in Betracht ziehe, die ist, diese Methoden für die Implementierung der Instanzmethoden mit „default“-Implementierungen direkt im Interface anzubieten

interface MutableDoubleTuple ... {
    default void add(DoubleTuple other) {
        DoubleTuples.add(this, other, this);
    }
}

[ot]
Offtopiception:

Und wem jetzt die Frage auf der Zunge liegt, welche Methoden es mit welchen Semantiken geben sollte…
a.add(b); // a += b
a.add(b, c); // a = b + c ?
c = a.add(b); // c = a + b
...

der wird vielleicht zu ähnlichen Selbstzweifeln und schlaflosen Nächten gelangen, wie ich, und am Ende zu der Einsicht, dass die Funktionalität, zwei (immutable) Tuples zu addieren und das Ergebnis in ein drittes (mutables) zu schreiben kanonisch, universell, „minimal“ und all diesen Funktionen gemein ist - und damit ja … irgendwie ausgelagert werden könnte… oder sollte… …)
[/ot]
[/ot]

Was mich dann aber an der Readme dieses so auf „Schönheit“, „Best Practices“ und „Prinzipien“ pochenden Projektes irritiert hat, waren die Codesnippets:

public final class App {
  public static void main(final String... args) throws Exception {
    new FtBasic(
      new TkFork(new FkRegex("/", "hello, world!")), 8080
    ).start(Exit.NEVER);
  }
}

:eek: new, new, new überall!

Eine „Best practice“ sehe ich persönlich darin: Keine public classes mit public constructors!. Zumindest wird man in o.g. „ND“-library keine öffentliche Klasse mit öffentlichem Konstruktor finden, die direkt mit „new“ instantiiert wird. Ich will das nicht mit Item 1 aus Effective Java begründen:
Consider static factory methods instead of constructors
(Ja, „consider“ heißt „consider“, und nicht „always use“). Aber ich finde das Muster von
interface MyModel { ... }
/*not public*/ class DefaultMyModel implements MyModel { ... }
public class MyModels { public static MyModel create() { ... } }
so klar, einfach, sauber und „etabliert“, dass es selten Gründe gibt, davon abzuweichen.

Nach einer kurzen Suche (z.B. bei Guice) sieht es so aus, als wäre sowas ja nur mit einigen Krämfen und Workarounds zu Injecten -_- Aber nochmal genauer schauen…

Mich auch nicht, hab aber auch nur den Text überflogen und nicht das Video angesehen (lohnt es sich?).

Ich finde null nicht so dramatisch, hängt imho vom Kontext ab. Allerdings scheint es in der Praxis mehr Probleme zu geben als ich mir vorstellen konnte: BBC - Future - These unlucky people have names that break computers

Na, ob das ab gefrühstückt ist, ist relativ. Ich wüste nicht wofür ich statische Methoden bräuchte. :wink:

Mit DI brauchst Du kein new. „DI is the new new!“ Ich würde trotzdem eine ArrayList mit new instantiieren, sehe aber auch kein wirkliches Problem darin wenn man das mit DI machen würde. Evtl. bekommt der Konstruktor zu viele Parameter, so das es unübersichtlicher wird. Musste man ggf. mal in einem kleineren Projekt ausprobieren.

[QUOTE=Marco13;131783]Eine „Best practice“ sehe ich persönlich darin: Keine public classes mit public constructors!. Zumindest wird man in o.g. „ND“-library keine öffentliche Klasse mit öffentlichem Konstruktor finden, die direkt mit „new“ instantiiert wird. Ich will das nicht mit Item 1 aus Effective Java begründen:
Consider static factory methods instead of constructors[/quote]

Wenn ich mal von meiner Seite zitiere:

(Da sollte „-Make the interfaces public, the classes package private, and the constructors private.“ stehen, naja, immerhin ein Bug gefunden ;))

Factories kannst Du bei DI entweder selber schreiben (ohne statische Methoden) und als Singleton Scope injizieren oder das DI Framework erzeugt die Factory automatisch, dann musst Du nur das Interface angeben.
Da javax.inject.Inject unabhängig vom DI Framework ist, kannst Du den Konstruktor auch mit @Inject annotieren, sodass das DI Framework die Klasse injizieren kann und zusätzlich Deine statischen factories bereitstellen für die, die kein DI verwenden wollen.

Gruß
Fancy

Das Video habe ich auch nicht gesehen - dazu hat mich schon der Text zu wenig überzeugt :o

(Über den Link mit Herrn Null bin ich kürzlich auch gestolpert … erinnert an den (in diesem Zusammenhang 1000 mal verlinkten) Klassiker xkcd: Exploits of a Mom )

„DI ist das neue new“ ist ein weiter Grund, wegen der vielen „news“ irritiert zu sein. Mir geht es dabei eher um die Sichtbarkeit und Spezifischkeit: Die Klasse (die konkrete Klasse und ihr Konstruktor) müssen dafür bekannt sein. Eigentlich sind Klassen ja per Definition ein „Implementierungsdetail“ :smiley: WENN eine (konkrete) Klasse public sein muss, dann sollte sie IMHO final sein, damit niemand Mist machen kann (Da gibt’s aber nur wenige Fälle. Klassisch: Events, oder vielleicht irgendwas Bean-artiges).

Worauf mein letzter Einlass eigentlich abzielte: Wenn die konkrete Klasse nicht bekannt ist, könnte DI schwierig werden. Ich hatte beim Überfliegen was von Factories gesehen, aber da ging es immer um Factory-Objekte, und nicht um static factory methods. Aber da muss ich mich noch einlesen.

(Und … was ist an nanojl besser als an guice? :D)

Hm, da weiß ich nicht genau was Du meinst. Nochmal Dein Utilities Beispiel, diesmal vollständig (außer Tests) damit wir nicht aneinander vorbeischreiben:

Alle drei in einem package:



public interface Utilities {

    int add(int a, int b);

}```
```package showcase.utilities;


import javax.inject.Inject;
import javax.inject.Singleton;

import org.slf4j.Logger;


@Singleton
final class UtilitiesImpl implements Utilities {

    private final Logger logger;


    @Inject
    private UtilitiesImpl(final Logger logger) {

        this.logger = logger;

    }


    @Override
    public int add(final int a, final int b) {

        final int result = a + b;

        logger.debug("add({},{}) results in {}", a, b, result);

        return result;

    }


}```
```package showcase.utilities;


import javax.inject.Inject;

import net.nanojl.injector.Injector;
import net.nanojl.injector.Module;


public final class UtilitiesModule implements Module {

    private final Injector injector;


    @Inject
    private UtilitiesModule(final Injector injector) {

        this.injector = injector;

    }


    @Override
    public void config() {

        injector.bind(Utilities.class, UtilitiesImpl.class);

    }


}```

Außerhalb des package sind nur das Interface und das Module sichtbar, nicht die Implementierung.

Möchte man das nun verwenden, sieht das so aus:

```package showcase.main;


import javax.inject.Inject;

import org.slf4j.Logger;

import net.nanojl.injector.JlInjector;
import showcase.utilities.Utilities;
import showcase.utilities.UtilitiesModule;


public final class Main implements Runnable {

    private final Logger logger;
    private final Utilities utilities;


    @Inject
    private Main(final Logger logger, final Utilities utilities) {

        this.logger = logger;
        this.utilities = utilities;

    }


    @Override
    public void run() {

        logger.info("it is {}!", utilities.add(40, 2));

    }


    public static void main(final String[] args) {

        new JlInjector(UtilitiesModule.class).run(Main.class);

    }


}```

Beim ausführen passiert dann das:


00:06:55.651 [main] DEBUG net.nanojl.injector.JlConstructor - build instance of class showcase.utilities.UtilitiesModule via class showcase.utilities.UtilitiesModule for class net.nanojl.injector.JlInjector
00:06:55.662 [main] DEBUG net.nanojl.injector.JlConstructor - build instance of class showcase.main.Main via class showcase.main.Main for class net.nanojl.injector.JlInjector
00:06:55.662 [main] DEBUG net.nanojl.injector.JlConstructor - build instance of interface org.slf4j.Logger via class net.nanojl.injector.JlSlf4jLoggerProvider for class showcase.main.Main
00:06:55.662 [main] DEBUG net.nanojl.injector.JlConstructor - build instance of interface showcase.utilities.Utilities via class showcase.utilities.UtilitiesImpl for class showcase.main.Main
00:06:55.662 [main] DEBUG net.nanojl.injector.JlConstructor - build instance of interface org.slf4j.Logger via class net.nanojl.injector.JlSlf4jLoggerProvider for class showcase.utilities.UtilitiesImpl
00:06:55.664 [main] DEBUG showcase.utilities.UtilitiesImpl - add(40,2) results in 42
00:06:55.664 [main] INFO showcase.main.Main - it is 42!




[QUOTE=Marco13;131821](Und ... was ist an nanojl besser als an guice? :D)[/QUOTE]

Es ist leichtgewichtig und bietet nur die guten features. ;)


Gruß
Fancy

OK, da scheint das “Module” die entscheidende Rolle zu spielen - wie gesagt, ich muss mir das alles mal anschauen, vorher macht es wohl nicht viel Sinn, über die Vor/Nachteile verschiedener DI-Lösungen zu reden. (Bin nur kürzlich drüber gestoplert, dass z.B. in Eclipse 4 auch alles über DI gemacht wird - das hat schon was magisches… man kann sich zwar grob vorstellen, was da intern abläuft, aber in dieser Vorstellung wirkt es wie eine übergeordnete Gottgleiche Entität, was erstmal suspekt ist… Vielleicht bin ich auch einfach altmodisch. Ich ess’ jetzt erstmal ein Raider, und schau’ mir das dann genauer an)