Try using it in your preferred language.

English

  • English
  • 汉语
  • Español
  • Bahasa Indonesia
  • Português
  • Русский
  • 日本語
  • 한국어
  • Deutsch
  • Français
  • Italiano
  • Türkçe
  • Tiếng Việt
  • ไทย
  • Polski
  • Nederlands
  • हिन्दी
  • Magyar
translation

Dies ist ein von KI übersetzter Beitrag.

제이온

[Effektives Java] Item 1. Statische Fabrikmethoden in Betracht ziehen

  • Schreibsprache: Koreanisch
  • Referenzland: Alle Länder country-flag

Sprache auswählen

  • Deutsch
  • English
  • 汉语
  • Español
  • Bahasa Indonesia
  • Português
  • Русский
  • 日本語
  • 한국어
  • Français
  • Italiano
  • Türkçe
  • Tiếng Việt
  • ไทย
  • Polski
  • Nederlands
  • हिन्दी
  • Magyar

Von durumis AI zusammengefasster Text

  • Statische Fabrikmethoden sind eine Möglichkeit, Instanzen einer Klasse zu erstellen, anstatt Konstruktoren zu verwenden. Sie haben den Vorteil, dass sie Instanzen einen Namen geben können, die Leistung durch Caching von Objekten mit hohen Erstellungskosten verbessern und bei Bedarf andere Untertypen von Objekten zurückgeben können.
  • Vor Java 8 war es nicht möglich, statische Methoden in Schnittstellen zu deklarieren, daher wurde eine Begleitklasse erstellt, um statische Fabrikmethoden zu definieren. Nach Java 8 können statische Methoden direkt zu Schnittstellen hinzugefügt werden, sodass es nicht mehr erforderlich ist, Begleitklassen separat zu definieren.
  • Die Verwendung statischer Fabrikmethoden fördert die Komposition anstelle der Vererbung und kann sogar ein Vorteil sein, wenn es darum geht, unveränderliche Typen zu erstellen, da diese Einschränkung eingehalten werden muss. Da statische Fabrikmethoden jedoch nicht so deutlich in der API-Dokumentation wie Konstruktoren erscheinen, müssen Entwickler die API-Dokumentation gut schreiben und die Methoden mit allgemein bekannten Konventionen benennen, um Probleme zu minimieren.

Überblick

Die traditionelle Methode, um eine Instanz einer Klasse zu erhalten, ist der öffentliche Konstruktor.


public class Member {

    private String name;

    private int age;

    private String hobby;

    private MemberStatus memberStatus;

    public Member(String name, int age, String hobby, MemberStatus memberStatus) {
        this.name = name;
        this.age = age;
        this.hobby = hobby;
        this.memberStatus = memberStatus;
    }
}

public enum MemberStatus {

    ADVANCED,
    INTERMEDIATE,
    BASIC;


Normalerweise reichen öffentliche Konstruktoren aus, aber wenn neben Konstruktoren auch statische Factory-Methoden (static factory method) bereitgestellt werden, ist es für Benutzer oft einfacher, Instanzen gemäß ihren Absichten zu erstellen.


Ein typisches Beispiel für statische Factory-Methoden ist die valueOf()-Methode von Boolean.


public static Boolean valueOf(boolean b) {
    return b ? Boolean.TRUE : Boolean.FALSE;


Diese Methode akzeptiert einen Wert vom Basistyp boolean und gibt ein Boolean-Objekt zurück.


Vorteile von statischen Factory-Methoden

Sie können einen Namen haben.

Die an den Konstruktor übergebenen Parameter und der Konstruktor selbst beschreiben die Eigenschaften des zurückgegebenen Objekts nicht ausreichend. Nehmen wir zum Beispiel die Hauptklasse des Member-Konstruktors (name, age, hobby, memberStatus). Es ist schwierig zu erkennen, welche Eigenschaften ein Mitglied hat, wenn man nur den Konstruktor betrachtet.


Außerdem kann man mit einer Signatur nur einen Konstruktor erstellen, während statische Factory-Methoden einen Namen haben und somit mit einer Signatur mehrere statische Factory-Methoden erstellen können, um Instanzen zurückzugeben.


public class Member {

    private String name;

    private int age;

    private String hobby;

    private MemberStatus memberStatus;

    public Member(String name, int age, String hobby, MemberStatus memberStatus) {
        this.name = name;
        this.age = age;
        this.hobby = hobby;
        this.memberStatus = memberStatus;
    }

    public static Member basicMember(String name, int age, String hobby) {
        return new Member(name, age, hobby, MemberStatus.BASIC);
    }

    public static Member intermediateMember(String name, int age, String hobby) {
        return new Member(name, age, hobby, MemberStatus.INTERMEDIATE);
    }

    public static Member advancedMember(String name, int age, String hobby) {
        return new Member(name, age, hobby, MemberStatus.ADVANCED);
    }


Wenn man mehrere statische Factory-Methoden mit der gleichen Signatur erstellt, anstatt den MemberStatus über den Konstruktor zu unterscheiden, kann der Benutzer eine Instanz eines Mitglieds mit bestimmten Fähigkeiten erstellen, ohne Verwirrung zu stiften.

Betrachten wir die in JDK definierte Bibliothek, so gibt es die statische Factory-Methode probablePrime() von BigInteger.


public static BigInteger probablePrime(int bitLength, Random rnd) {
    if (bitLength < 2)
        throw new ArithmeticException("bitLength < 2");

    return (bitLength < SMALL_PRIME_THRESHOLD ?
            smallPrime(bitLength, DEFAULT_PRIME_CERTAINTY, rnd) :
            largePrime(bitLength, DEFAULT_PRIME_CERTAINTY, rnd));


Wenn man den Standardkonstruktor von BigInteger mit der statischen Factory-Methode probablePrime() vergleicht, ist es klar, dass Letztere den Satz „Gibt ein BigInteger-Objekt zurück, dessen Wert eine Primzahl ist“ besser beschreibt.


Sie müssen bei jedem Aufruf keine neue Instanz erstellen.

public static Boolean valueOf(boolean b) {
    return (b ? Boolean.TRUE : Boolean.FALSE);


Man kann sehen, dass die valueOf()-Methode von Boolean eine Instanz zwischenspeichert und dann zurückgibt. Dieses Merkmal kann die Leistung erheblich verbessern, wenn Objekte mit hohen Erstellungskosten häufig angefordert werden.Flyweight-Musterkann als eine ähnliche Technik betrachtet werden.


Klassen, die statische Factory-Methoden verwenden, um bei wiederholten Anfragen dasselbe Objekt zurückzugeben, werden als Instanzsteuerungsklassen bezeichnet, da sie die Lebensdauer von Instanzen steuern können. Die Instanzsteuerung ermöglicht es, Singleton-Klassen zu erstellen oder Klassen zu erstellen, die nicht instanziiert werden können. Außerdem kann es in unveränderlichen Klassen sichergestellt werden, dass es nur eine einzige Instanz gibt.

Die Instanzsteuerung ist die Grundlage des Flyweight-Musters, und Aufzählungsdatentypen gewährleisten, dass nur eine einzige Instanz erstellt wird.


Beispiel

In Minecraft müssen Bäume gepflanzt werden. Wenn für jedes Baumobjekt ein neues Objekt erstellt wird, besteht die Gefahr eines Memory-Overflows.

Daher kann man die roten und grünen Baumobjekte speichern und nur ihre Positionen ändern, bevor man sie zurückgibt. Natürlich kann es mehr als zwei Farben geben, daher wäre es effizient, die Bäume nach Farbe in einer Datenstruktur wie einer Map zu speichern.


public class Tree {

    // Ein Baum hat die folgenden drei Informationen.
    private String color;
    private int x;
    private int y;

    // Konstruktor, der nur durch die Farbe erstellt wird.
    public Tree(String color) {
        this.color = color;
    }

    public void setX(int x) {
        this.x = x;
    }

    public void setY(int y) {
        this.y = y;
    }

    // Beim Pflanzen eines Baumes
    public void install(){
        System.out.println("x:"+x+" y:"+y+" An dieser Stelle wurde ein "+color+"-farbiger Baum installiert!");
    }
}

public class TreeFactory {
    // HashMap-Datenstruktur zum Verwalten von erstellten Bäumen.
    public static final Map treeMap = new HashMap<>();
    
   
    public static Tree getTree(String treeColor){
        // Suchen Sie in der Map nach einem Baum mit der eingegebenen Farbe. Wenn er vorhanden ist, geben Sie dieses Objekt zurück.
        Tree tree = (Tree)treeMap.get(treeColor); 

       // Wenn in der Map noch kein Baum mit der gleichen Farbe vorhanden ist, erstellen Sie ein neues Objekt und geben Sie es zurück.
        if(tree == null){
            tree = new Tree(treeColor);
            treeMap.put(treeColor, tree);
            System.out.println("Neues Objekt erstellt");
        }

        return tree;
    }
}

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        System.out.println("Geben Sie die gewünschte Farbe ein :)");
        for(int i=0;i<10;i++){
            // Baumfarbe eingeben
            String input = scanner.nextLine();
            // Holen Sie sich einen Baum aus der Fabrik.
            Tree tree = (Tree)TreeFactory.getTree(input);
            // Setzen Sie die Position x, y des Baumes.
            tree.setX((int) (Math.random()*100));
            tree.setY((int) (Math.random()*100));
            // Baum installieren.
            tree.install();
        }
    }


Unterschied zum Singleton-Muster

Das Singleton-Muster erlaubt es, nur einen einzigen Baum in der Baumklasse zu erstellen. Daher müsste man die Farbe des erstellten einzelnen Objekts ändern, wenn man das Singleton-Muster verwendet. Das bedeutet, dass man mit dem Singleton-Muster nur einen einzigen Baum haben kann, unabhängig von der Art.


Einsatzmöglichkeiten

Das Flyweight-Muster wird im String Constant Pool von Java verwendet.


Sie können ein Objekt eines Untertyps des Rückgabetyps zurückgeben.

Wenn Sie die asList()-Methode der Arrays-Dienstprogramm-Klasse verwendet haben, können Sie diesen Vorteil verstehen.


public static  List asList(T... a) [
    return new ArrayList<>(a);


Es wird ein Wert in eine ArrayList, eine Unterimplementierung von List, verpackt und zurückgegeben, wobei der Benutzer diese Implementierung nicht kennen muss. Mit anderen Worten, die Flexibilität, die Klasse des zurückgegebenen Objekts frei wählen zu können, ermöglicht es dem Entwickler, die Implementierung zu verbergen und die Implementierung zurückzugeben, wodurch die API klein gehalten werden kann.


Über statische Methoden in Java-Schnittstellen

Vor Java 8 konnten in Schnittstellen keine statischen Methoden deklariert werden. Wenn also eine statische Methode erforderlich war, die eine Schnittstelle mit dem Namen „Type“ zurückgab, wurde eine Begleitklasse namens „Types“ erstellt, die nicht instanziiert werden konnte und die Methode enthielt.


Ein bekanntes Beispiel sind die 45 Dienstprogramm-Implementierungen, die von JCF bereitgestellt werden, von denen die meisten über statische Factory-Methoden in einer einzigen Begleitklasse namens java.util.Collections erhalten werden. Insbesondere gibt es unter diesen Implementierungen einige, die nicht öffentlich sind und daher nur über statische Factory-Methoden instanziiert werden können (diese Implementierungen können natürlich nicht vererbt werden).


Außerdem werden die 45 Implementierungen nicht offengelegt, was zu einer viel kleineren API führt.


// Beispiel für eine Schnittstelle und eine Begleitklasse


Ab Java 8 können jedoch statische Methoden direkt zu Schnittstellen hinzugefügt werden, sodass keine separate Begleitklasse mehr definiert werden muss.


Es ist möglich, je nach Eingabeparameter ein Objekt einer anderen Klasse zurückzugeben.

Neben der einfachen Rückgabe eines Untertyps kann man je nach Wert des Parameters einen anderen Untertyp zurückgeben. Wenn man beispielsweise den MemberStatus je nach Punktzahl zurückgeben möchte, kann man eine statische Factory-Methode wie folgt erstellen und dort die Vergleichslogik einfügen.


public enum MemberStatus {

    ADVANCED(80, 100),
    INTERMEDIATE(50, 79),
    BASIC(0, 49);

    private final int minScore;
    private final int maxScore;

    MemberStatus(int minScore, int maxScore) {
        this.minScore = minScore;
        this.maxScore = maxScore;
    }

    public static MemberStatus of(int score) {
        return Arrays.stream(values())
                .filter(decideMemberStatus(score))
                .findAny()
                .orElseThrow(() -> new NoSuchElementException("Kein MemberStatus-Objekt gefunden."));
    }

    private static Predicate decideMemberStatus(int score) {
        return element -> element.minScore <= score && element.maxScore >= score;
    }
}

@DisplayName("MemberStatus-Test")
class MemberStatusTest {

    @ParameterizedTest
    @CsvSource(value = {"0:BASIC", "30:BASIC", "50:INTERMEDIATE", "70:INTERMEDIATE", "80:ADVANCED", "100:ADVANCED"}, delimiter = ':')
    @DisplayName("Gibt MemberStatus je nach Punktzahl unterschiedlich zurück.")
    void of(int input, MemberStatus expected) {
        assertThat(MemberStatus.of(input)).isEqualTo(expected);
    }


Zum Zeitpunkt des Schreibens der statischen Factory-Methode muss die Klasse des zurückzugebenden Objekts nicht existieren.

Im obigen Satz bezieht sichKlasse des Objektsauf unsere erstellte Klassendatei.

Als Hinweis: Class bezieht sich auf das Class-Objekt, das vom Classloader beim Laden einer Klasse auf dem Heap zugewiesen wird. Dieses Class-Objekt enthält verschiedene Metadaten unserer erstellten Klasse.


package algorithm.dataStructure;

public abstract class StaticFactoryMethodType {

    public abstract void getName();

    public static StaticFactoryMethodType getNewInstance() {
        StaticFactoryMethodType temp = null;
        try {
            Class childClass = Class.forName("algorithm.dataStructure.StaticFactoryMethodTypeChild"); // Reflexion
            temp = (StaticFactoryMethodType) childClass.newInstance(); // Reflexion

        } catch (ClassNotFoundException e) {
           System.out.println("Klasse nicht gefunden.");
        } catch (InstantiationException  e) {
            System.out.println("Kann nicht im Speicher platziert werden.");
        } catch (IllegalAccessException  e) {
            System.out.println("Fehler beim Zugriff auf die Klassendatei.");
        }

        return temp;
    }


Im obigen Code kann man sehen, dass ein Class-Objekt über die Position der Implementierung der Schnittstelle erstellt wird und die tatsächliche Implementierung mithilfe der Reflexionstechnik initialisiert wird. Zu diesem Zeitpunktzum Zeitpunkt des Schreibens der statischen Factory-Methodemuss die Klasse StaticFactoryMethodTypeChild nicht existieren.


Wenn zum Zeitpunkt der Verwendung der statischen Factory-Methode keine Implementierung im Pfad algorithm.dataStructure.StaticFactoryMethodTypeChild vorhanden ist, tritt ein Fehler auf. Aber zum Zeitpunkt des Schreibens der statischen Factory-Methode gibt es kein Problem, daher ist es flexibel.


public interface Test {

    int sum(int a, int b);

    // Test ist eine Schnittstelle und es gibt keine Implementierung, daher tritt zum Zeitpunkt des Schreibens der statischen Factory-Methode kein Problem auf.
    static Test create() {
        return null;
    }
}

public class Main {

    public static void main(String[] args) {
        Test test = Test.create();
        System.out.println(test.sum(1, 2)); // NPE tritt auf
    }


Man kann dieselbe Flexibilität erreichen, ohne Reflexion zu verwenden. Die statische Factory-Methode create() von Test zeigt, dass zum Zeitpunkt des Schreibens kein Problem auftritt, auch wenn keine Implementierung vorhanden ist. Natürlich tritt zum Zeitpunkt der tatsächlichen Verwendung eine NPE auf, daher muss später eine Implementierung zurückgegeben werden.


Diese Flexibilität ist die Grundlage für die Erstellung von Service Provider Frameworks, von denen JDBC ein typisches Beispiel ist. Der Anbieter eines Service Provider Frameworks ist die Implementierung des Service, und das Framework steuert die Bereitstellung dieser Implementierungen an den Client, um den Client von der Implementierung zu trennen (DIP).


  • Komponenten eines Service Provider Frameworks
    • Service-Schnittstelle
      • Definiert das Verhalten der Implementierung.
      • JDBC-Verbindung
    • API zur Anbieterregistrierung
      • Der Anbieter registriert die Implementierung.
      • JDBC-DriverManager.registerDriver()
    • API zum Zugriff auf Dienste
      • Wird verwendet, wenn der Client eine Instanz des Service abruft. Wenn keine Bedingung angegeben wird, wird die Standardimplementierung oder die unterstützten Implementierungen abwechselnd zurückgegeben.
      • Relevanz von statischen Factory-Methoden
      • JDBC-DriverManager.getConnection()
    • (optional) Service Provider Interface
      • Wenn dies nicht vorhanden ist, muss die Reflexion verwendet werden, um jede Implementierung zu instanziieren.
      • JDBC-Treiber


Das Service Provider Framework-Muster hat viele Variationen, darunter das Bridge-Muster, Dependency Injection Frameworks usw.


Typisches JDBC-Beispiel

Class.forName("oracle.jdbc.driver.OracleDriver"); 
Connection connection = null; 
connection = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:ORA92", "root", "root"); 


Normalerweise wird JDBC wie oben geschrieben. Class.forName() wird verwendet, um OracleDriver zu registrieren, eine der Implementierungen von Driver. DriverManager.getConnection() wird verwendet, um eine Verbindung zur Implementierung von OracleDriver zu erhalten, eine der Implementierungen von Connection.


Dabei ist Connection die Service-Schnittstelle, DriverManager.getConnection() ist die Service-Zugriffs-API und Driver ist die Service Provider-Schnittstelle. Die API zur Anbieterregistrierung, DriverManager.registerDriver(), wurde jedoch nicht verwendet. Trotzdem können wir OracleDriver, eine Implementierung von Driver, mit Class.forName() allein registrieren. Wie ist das möglich?


Funktionsweise von Class.forName()

Wenn man diese Methode mit dem Namen der physischen Klassendatei als Argument aufruft, wird die JVM aufgefordert, diese Klasse zu laden. Der Classloader speichert die Metadaten der Klasse im Methodenbereich und weist gleichzeitig ein Class-Objekt auf dem Heap zu. Außerdem wird nach Abschluss des Klassengeladensder statische Feld- und Blockinitialisierungsvorgang durchgeführt, und in diesem Schritt wird die API zur Anbieterregistrierung verwendet.


public class OracleDriver implements Driver {

    static {
        defaultDriver = null;
        Timestamp timestamp = Timestamp.valueOf("2000-01-01 00:00:00.0");
        try {
            if (defaultDriver == null) {
                defaultDriver = new OracleDriver();
                DriverManager.registerDriver(defaultDriver); // OracleDriver registrieren
            }
        } catch (RuntimeException runtimeexception) {
        } catch (SQLException sqlexception) {
        }
    }

    ...


Tatsächlich kann man sehen, dass in OracleDriver im statischen Block DriverManager.registerDriver() verwendet wird, um OracleDriver, die Implementierung von Driver, zu registrieren.


Analyse der DriverManager-Klasse

public class DriverManager {

    private DriverManager() {
    }

    private static final Map drivers = new ConcurrentHashMap();
    public static final String DEFAULT_DRIVER_NAME = "default";

    public static void registerDefaultPrivider(Driver d) {
        System.out.println("Driver registrieren");
        registerDriver(DEFAULT_DRIVER_NAME, d);
    }

    public static void registerDriver(String name, Driver d) {
        drivers.put(name, d);
    }

    public static Connection getConnection() {
        return getConnection(DEFAULT_DRIVER_NAME);
    }

    public static Connection getConnection(String name) {
        Driver d = drivers.get(name);
        if (d == null) throw new IllegalArgumentException();
        return d.getConnection();
    }


Die DriverManager-Klasse ist in Wirklichkeit viel komplexer, aber wenn man sich auf das Wesentliche konzentriert, ist sie der oben beschriebenen ähnlich. Wie bereits erwähnt, wird registerDriver() in der statischen Blockanweisung von OracleDriver aufgerufen, um OracleDriver zu registrieren, und der Benutzer kann eine Implementierung von Connection abrufen, indem er getConnection() aufruft.


Wenn man sich die Service-Zugriffs-API getConnetion() genauer ansieht, stellt man fest, dass eine Verbindung aus der Driver-Schnittstelle abgerufen wird. Wenn es die Service Provider-Schnittstelle Driver nicht gäbe, müsste man Reflexion wie Class.forName() verwenden, um die gewünschte Connection-Implementierung zurückzugeben. Zu diesem Zeitpunktmuss die Connection-Implementierung zum Zeitpunkt der Erstellung der statischen Factory-Methode nicht existieren.


Stattdessen verwenden wir die Driver-Schnittstelle, registrieren dynamisch die Implementierung von Driver und können dann einfach eine Implementierung von Connection abrufen, die zu diesem Driver passt.


Als Hinweis: Ich habe den tatsächlichen JDK-Code der getConnection()-Methode von DriverManager analysiert. Wenn Sie nicht daran interessiert sind, können Sie dies überspringen.


@CallerSensitive
public static Connection getConnection(String url,
    String user, String password) throws SQLException {
    java.util.Properties info = new java.util.Properties();

    if (user != null) {
        info.put("user", user);
    }
    if (password != null) {
        info.put("password", password);
    }

    return (getConnection(url, info, Reflection.getCallerClass()));


Zuerst wird die öffentliche statische Methode getConnection() aufgerufen, wobei url, Properties und CallerClass als Argumente an die private statische Methode getConnection() übergeben werden. Reflection.getCallerClass() dient dazu, die Klasse abzurufen, die die öffentliche statische Methode getConnection() aufgerufen hat. Wenn die Klasse Car getConnection() aufgerufen hat, kann Reflection.getCallerClass() das Class-Objekt abrufen.


private static Connection getConnection(String url, java.util.Properties info, Class caller) throws SQLException {
    ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
    synchronized(DriverManager.class) {
        if (callerCL == null) {
            callerCL = Thread.currentThread().getContextClassLoader();
        }
    }

    if(url == null) {
        throw new SQLException("Die URL darf nicht null sein", "08001");
    }

    SQLException reason = null;
    for(DriverInfo aDriver : registeredDrivers) {
        if(isDriverAllowed(aDriver.driver, callerCL)) {
            try {
                Connection con = aDriver.driver.connect(url, info);
                if (con != null) {
                    return (con);
                }
            } catch (SQLException ex) {
                if (reason == null) {
                    reason = ex;
                }
            }
        }
    }

    if (reason != null)    {
        throw reason;
    }
    throw new SQLException("Kein geeigneter Treiber für "+ url gefunden", "08001");


callerCL ist ein Classloader-Objekt, das vom Classloader von caller oder dem aktuellen Thread erstellt wird. Anschließend wird aDriver nacheinander aus registeredDrivers, der Liste der in der aktuellen Anwendung registrierten Treiber, entnommen. Wenn dieser Driver von isDriverAllowed() als true erkannt wird, wird mithilfe dieses Drivers ein Connection-Objekt abgerufen und zurückgegeben. isDriverAllowed() prüft, ob aDriver in caller vorhanden ist.


Vorteile des JDBC-Frameworks

Der springende Punkt des JDBC-Frameworks ist, dass die Schnittstellen Driver und Connection sowie die Klassen, die diese Schnittstellen implementieren, vollständig getrennt bereitgestellt werden. Indem man die Schnittstellen verwendet, um einen Rahmen zu erstellen, und dann separate Implementierungsklassen zu diesem Rahmen erstellt, erhält man einen sehr flexiblen Ansatz.


So kann jeder DBMS-Anbieter die Schnittstellen Driver und Connection implementieren und diese bereitstellen, so dass Java-Entwickler dieselbe API für verschiedene DBMS-Treiber verwenden können.


Nachteile von statischen Factory-Methoden

Wenn man erbt, benötigt man einen öffentlichen oder geschützten Konstruktor. Wenn man nur statische Factory-Methoden bereitstellt, kann man keine Unterklassen erstellen.


Diese Einschränkung ist jedoch eher ein Vorteil, da sie eher zu Komposition als zu Vererbung führt und diese Einschränkung eingehalten werden muss, um unveränderliche Typen zu erstellen.


Statische Factory-Methoden sind für Programmierer schwer zu finden.

Da sie nicht so deutlich in der API-Beschreibung wie Konstruktoren dargestellt werden, müssen Entwickler die API-Dokumentation gut schreiben und die Methoden benennen, indem sie allgemein bekannte Konventionen befolgen, um das Problem zu entschärfen.


Benennung von statischen Factory-Methoden

  • from
    • Nimmt ein Argument entgegen und gibt eine Instanz dieses Typs zurück.
    • Date date = Date.from(instant);
  • of
    • Nimmt mehrere Argumente entgegen und gibt eine Instanz des entsprechenden Typs zurück.
    • Set<Rank> faceCards = EnumSet.of(JACK, QUEEN, KING);
  • valueOf
    • Detailliertere Version von from und of.
    • BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);
  • instance oder getInstance
    • Gibt die Instanz zurück, die durch den Parameter angegeben wird, garantiert aber nicht, dass es sich um dieselbe Instanz handelt.
    • StackWalker luke = StackWalker.getInstance(options);
  • create oder newInstance
    • Wie instance oder getInstance, garantiert aber, dass jedes Mal eine neue Instanz erstellt und zurückgegeben wird.
    • Object newArray = Array.newInstance(classObject, arraylen);
  • getType
    • Wie getInstance, wird aber verwendet, wenn die Factory-Methode nicht in der zu erstellenden Klasse, sondern in einer anderen Klasse definiert ist.
    • FileStore fs = Files.getFileStore(path);
  • newType
    • Wie newInstance, wird aber verwendet, wenn die Factory-Methode nicht in der zu erstellenden Klasse, sondern in einer anderen Klasse definiert ist.
    • BufferedReader br = Files.newBufferedReader(path);
  • type
    • Kompakte Version von getType und newType.
    • List<Complaint> litany = Collections.list(legacyLitany);


Zusammenfassung

Statische Factory-Methoden und öffentliche Konstruktoren haben jeweils ihren eigenen Zweck, daher sollten Sie sie entsprechend verwenden.


Quelle

제이온
제이온
제이온
제이온
[Effektives Java] Punkt 5: Verwenden Sie Dependency Injection anstelle von expliziten Ressourcen Wenn eine Klasse von externen Ressourcen abhängt, sollten Singleton- und statische Utility-Klassen vermieden werden. Durch Dependency Injection können Sie die Flexibilität, Wiederverwendbarkeit und Testbarkeit der Klasse verbessern. Die Verwendung des Fac

28. April 2024

[Effektives Java] Artikel 2. Erwägen Sie einen Builder, wenn der Konstruktor viele Parameter hat Wenn Sie ein Objekt mit vielen Parametern erstellen, können Sie den Builder-Pattern verwenden, um den Code übersichtlicher und lesbarer zu machen. Erstellen Sie ein Builder-Objekt mit den erforderlichen Parametern, stellen Sie die optionalen Parameter mit

27. April 2024

[Effektives Java] Artikel 3. Garantieren Sie Singleton mit einem privaten Konstruktor oder einem Aufzähltyp Dieser Artikel stellt drei Methoden zur Implementierung des Singleton-Musters in Java vor (öffentliches statisches Mitglied, statische Fabrikmethode, Aufzähltyp) und erläutert die Vor- und Nachteile jeder Methode sowie Vorsichtsmaßnahmen bei der Serialisi

27. April 2024

[Javascript] Objektstruktur (V8) Das JavaScript-Objekt wird in der V8-Engine je nach Zustand als strukturierte, optimierte Fast-Mode oder als Hashmap-basierter Dictionary-Mode dargestellt. Der Fast-Mode ist schnell, wenn Schlüssel und Werte fast fixiert sind, aber bei der Hinzufügung neu
곽경직
곽경직
곽경직
곽경직
곽경직

18. März 2024

[Nicht-Hauptfach, Überleben als Entwickler] 14. Zusammenfassung der häufigen technischen Vorstellungsgesprächsinhalte für Einsteiger Dieser Leitfaden ist für die Vorbereitung auf technische Vorstellungsgespräche für Einsteiger. Hauptspeicherbereich, Datenstrukturen, RDBMS und NoSQL, prozedurale und objektorientierte Programmierung, Überladen und Überschreiben, Seitenersatzzustände, Pro
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자

3. April 2024

AI Full Stack mit Open Source realisieren Im Ökosystem der künstlichen Intelligenz tauchen immer mehr Open-Source-LLM-Modelle (Large Language Models) auf. Modelle wie Mistral, Llama und phi-2 mit leistungsstarker Performance und einer Open-Source-Lizenz wurden veröffentlicht und es werden ständig
RevFactory
RevFactory
RevFactory
RevFactory

5. Februar 2024

Wie Rust Konsistenzfehler verhindert Rust ist eine leistungsstarke Sprache, die die Herausforderungen der konkurrenten Programmierung bewältigt. Dank des Typsystems und des Besitzmodells ist der Datenaustausch und die gemeinsame Nutzung zwischen Threads sicher. Durch Muster der internen Vari
곽경직
곽경직
곽경직
곽경직
곽경직

28. März 2024

Logisches Datenmodellieren Logisches Datenmodellieren ist der Prozess der Transformation eines konzeptionellen Datenmodells in das relationale Datenbankparadigma unter Verwendung von Mapping-Regeln. Dabei werden 1:1-, 1:N- und N:M-Beziehungen verarbeitet und die Datenintegrität dur
제이의 블로그
제이의 블로그
제이의 블로그
제이의 블로그
제이의 블로그

9. April 2024

[Concurrency] Atomarer Vorgang: Memory Fence und Memory Ordering Dieser Blogbeitrag erklärt, wie bei atomaren Operationen die Reihenfolge im Speicher berücksichtigt wird, und die Bedeutung von Ordering-Optionen. Es werden verschiedene Ordering-Optionen wie Relaxed, Acquire, Release, AcqRel, SecCst erläutert und die Vor
곽경직
곽경직
곽경직
곽경직
곽경직

12. April 2024