Wyjaśnienie metod wytwórczych

0

Mam problem ze zrozumieniem tak zwanych metod wytwórczych.
Nie rozumiem z jakiego powodu używa się metod statycznych do tworzenia nowych instancji klasy, zamiast zrobić to jawnie z konstruktorem.
Dla przykładu mam taki kod:

		NumberFormat nf = NumberFormat.getCurrencyInstance();
		double x = 0.1;
		System.out.println(nf.format(x));

Z tego co się kiedyś dowiedziałem (możliwe, że tutaj na forum), to klasa NumberFormat ma prywatny konstruktor a nowa instancja powstaje po wywołaniu metody getCurrencyInstance.
Dlaczego nie odbywa się to za pomocą ogólnie dostępnego konstruktora tylko jest on prywatny?

Doczytałem w książce, że chodzi o ograniczenia przez które nie można utworzyć dwóch takich samych konstruktorów, ale nie rozumiem jak to się odnosi do podanego przykładu.

0

Pogrzebałem trochę w klasie NumberFormat i jest co najmniej jeden powód na użycie metod wytwórczych - prywatna metoda wytwórcza może zwrócić nulla (niby nieładnie, ale ten chyba nie wychodzi poza prywatne metody) i na tej podstawie inne (korzystające z niej) metody wytwórcze mogą podejmować kolejne próby utworzenia klasy. Konkretny kod z biblioteki standardowej:

    private static NumberFormat getInstance(Locale desiredLocale,
                                           int choice) {
        LocaleProviderAdapter adapter;
        adapter = LocaleProviderAdapter.getAdapter(NumberFormatProvider.class,
                                                   desiredLocale);
        NumberFormat numberFormat = getInstance(adapter, desiredLocale, choice);
        if (numberFormat == null) {
            numberFormat = getInstance(LocaleProviderAdapter.forJRE(),
                                       desiredLocale, choice);
        }
        return numberFormat;
    }

Konstruktory nie są tak elastyczne jak metody wytwórcze, bo konstruktor niczego nie może zwrócić - inicjalizuje on tylko obiekt po wskaźnikiem this.

Inną zaletą metod wytwórczych jest to, że prywatną implementację (podklasę) publicznej klasy można ukryć i jako typ zwracany w metodzie wytwórczej ustawić jakiś publiczny typ.

Ja natomiast używam metod wytwórczych by zawrzeć w nich efekty uboczne, gdyż wstawianie ich (efektów ubocznych, jak np rzucania wyjątku) w konstruktor utrudnia testowanie klasy, a tego bym nie chciał.

0

@Wibowit:
A czy jest jakiś ogólny powód na używanie metod wytwórczych? O ile dobrze rozumiem temat "... metody wytwórcze, czyli metody statyczne, które zwracają nowe instancje klasy.".
Jaki jest powód na tworzenie klasy z prywatnym konstruktorem i tworzeniem instancji dopiero dzięki konkretnej metodzie?

Z tego co udało mi się wyczytać (i zrozumieć) to pozwala to na użycie kilka razy takich samych konstruktorów - z takimi samymi parametrami?

0

Podałem kilka powodów. Jak chcesz je zebrać w jeden ogólny to proszę: metody wytwórcze są bardziej elastyczne od konstruktorów.

Z tego co udało mi się wyczytać (i zrozumieć) to pozwala to na użycie kilka razy takich samych konstruktorów - z takimi samymi parametrami?

Skoro już rozumiesz to podaj przykład.

0

Nie jestem pewien do końca czy dobrze to rozumiem. Przy pisaniu kodu związanego z kalendarzem użyłem czegoś takiego:javaCalendar C1 = Calendar.getInstance();.
Domyślam się, że w tej metodzie jest konstruktor ale nie rozumiem czemu zostało to rozwiązane w taki sposób?

2

Calendar.getInstance woła taką metodę:

    private static Calendar createCalendar(TimeZone zone,
                                           Locale aLocale)
    {
        CalendarProvider provider =
            LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale)
                                 .getCalendarProvider();
        if (provider != null) {
            try {
                return provider.getInstance(zone, aLocale);
            } catch (IllegalArgumentException iae) {
                // fall back to the default instantiation
            }
        }

        Calendar cal = null;

        if (aLocale.hasExtensions()) {
            String caltype = aLocale.getUnicodeLocaleType("ca");
            if (caltype != null) {
                switch (caltype) {
                case "buddhist":
                cal = new BuddhistCalendar(zone, aLocale);
                    break;
                case "japanese":
                    cal = new JapaneseImperialCalendar(zone, aLocale);
                    break;
                case "gregory":
                    cal = new GregorianCalendar(zone, aLocale);
                    break;
                }
            }
        }
        if (cal == null) {
            // If no known calendar type is explicitly specified,
            // perform the traditional way to create a Calendar:
            // create a BuddhistCalendar for th_TH locale,
            // a JapaneseImperialCalendar for ja_JP_JP locale, or
            // a GregorianCalendar for any other locales.
            // NOTE: The language, country and variant strings are interned.
            if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
                cal = new BuddhistCalendar(zone, aLocale);
            } else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
                       && aLocale.getCountry() == "JP") {
                cal = new JapaneseImperialCalendar(zone, aLocale);
            } else {
                cal = new GregorianCalendar(zone, aLocale);
            }
        }
        return cal;
    }

Jak byś chciał to zastąpić jednym konstruktorem?

1 użytkowników online, w tym zalogowanych: 0, gości: 1