Fluent builder - jak i czy wymuszać ustawianie pól?

Fluent builder - jak i czy wymuszać ustawianie pól?
PA
  • Rejestracja:prawie 8 lat
  • Ostatnio:9 dni
  • Postów:132
0

Witam. Ostatnio interesowałem się tematem fluent buildera. Z tego co zrozumiałem przy wielu polach dla danego obiektu pojawia się problem. Jeśli będziemy chcieli je wszystkie ustawiać w konstruktorze, to konstruktor będzie nieczytelny. Z kolei jeśli będziemy chcieli ustawiać pola przez settery to możemy o jakichś zapomnieć i stworzyć obiekt z niezadeklarowanymi wartościami dla pól. Nie rozumiem za bardzo jednak jak fluent builder rozwiązuje ten drugi problem. Jeśli stworzę sobie klasę:

Kopiuj
public class Person {

    private String name;
    private String surname;
    private int age;

    private Person(){}

    public String toString()
    {
        return "name: "+name+" surname: "+surname+" age: "+age;
    }

    public static final class Builder{

        private String name;
        private String surname;
        private int age;

        public Builder name (String name){
            this.name = name;
            return this;
        }

        public Builder surname (String surname){
            this.surname = surname;
            return this;
        }

        public Builder age (int age){
            this.age = age;
            return this;
        }

        public Person build(){
            Person person = new Person();
            person.name=name;
            person.surname=surname;
            person.age=age;
            return person;
        }
    }
}

Tworząc obiekt cały czas mogę ominąć ustawianie poszczególnych pól. Jeśli byłoby ich trzykrotnie więcej łatwo o tym po prostu zapomnieć, tak jak przy ustawianiu przez settery. Pobrałem parę projektów z GitHuba i w zasadzie w większości wygląda to tak samo. Raz widziałem żeby w tworzonym obiekcie były pola obowiązkowe i opcjonalne:

Kopiuj
public final class Email {

    // To Address. Multiple Address separated by ","
    String to;
    //From Address
    String from;
    // Subject of the email
    String subject;
    // Content of the email
    String content;
    // BCC optional
    String bcc;
    // CC Optional
    String cc;

    /**
     * Private constructor to prevent the object initialization
     */
    private Email(String to, String from, String subject, String content, String bcc, String cc) {
        this.to = to;
        this.from = from;
        this.subject = subject;
        this.content = content;
        this.bcc = bcc;
        this.cc = cc;
    }

    public String getTo() {
        return to;
    }

    public String getFrom() {
        return from;
    }

    public String getSubject() {
        return subject;
    }

    public String getContent() {
        return content;
    }

    public String getBcc() {
        return bcc;
    }

    public String getCc() {
        return cc;
    }

    @Override
    public String toString() {
        return "Email{" +
                "to='" + to + '\'' +
                ", from='" + from + '\'' +
                ", subject='" + subject + '\'' +
                ", content='" + content + '\'' +
                ", bcc='" + bcc + '\'' +
                ", cc='" + cc + '\'' +
                '}';
    }

    // Interface to Set From
    interface EmailFrom {
        EmailTo setFrom(String from);
    }
    //Interface to Set To
    interface  EmailTo {
        EmailSubject setTo(String to);
    }
    //Interface to Set subject
    interface  EmailSubject {
        EmailContent setSubject(String subject);
    }
    // Interface to set Content
    interface  EmailContent {
        EmailCreator setContent(String content);
    }
    // Final Email Creator Class
    interface EmailCreator {

        EmailCreator setBCC(String bcc);
        EmailCreator setCC(String cc);
        Email build();
    }

    /** Static class for building the email object
     */
    public static class EmailBuilder implements  EmailFrom, EmailTo, EmailSubject, EmailContent, EmailCreator{

        String to;
        String from;
        String subject;
        String content;
        String bcc;
        String cc;

        /**
         * Private emailbuilder to prevent direct object creation
         */
        private EmailBuilder(){
        }

        /**
         * Getting the instance method
         * @return
         */
        public static EmailFrom getInstance(){
            return  new EmailBuilder();
        }

        @Override
        public EmailTo setFrom(String from) {
            this.from = from;
            return this;
        }

        @Override
        public EmailSubject setTo(String to) {
            this.to = to;
            return this;
        }

        @Override
        public EmailContent setSubject(String subject) {
            this.subject=subject;
            return this;
        }

        @Override
        public EmailCreator setContent(String content) {
            this.content=content;
            return this;
        }

        @Override
        public EmailBuilder setBCC(String bcc) {
            this.bcc=bcc;
            return this;
        }

        @Override
        public EmailBuilder setCC(String cc) {
            this.cc =cc;
            return this;
        }

        @Override
        public Email build() {
            return new Email(to,from,subject,content,bcc, cc);
        }
    }
}

Tylko nie bardzo wiem czy to jest warte zachodu. Po mojemu traci na tym wszystkim czytelność. Czy przypadkiem nie jest tak, że bardziej w tych fluent builderach chodzi o to, żeby z jednej strony konstruktor nie był za duży, a z drugiej żeby nie było setterów po to by osiągnąć niemutowalność?

Shalom
  • Rejestracja:około 21 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Space: the final frontier
  • Postów:26433
0

żeby z jednej strony konstruktor nie był za duży

Nie chodzi tyle o rozmiar co o takie rzeczy jak np. problem z kolejnością parametrów. Wyobraź sobie konstruktor (int, int, int, int) który parametr jest który? Jaka szansa ze sie pomylisz? Builder pomaga tutaj bardzo. Dodatkowo co zrobisz jak połowa parametrów jest opcjonalna? Policz ile konstruktorów musisz napisać żeby obsłużyć 5 parametrów z których każdy można podać ale nie trzeba...

a z drugiej żeby nie było setterów po to by osiągnąć niemutowalność?

Tak, jedna z zalet jest taka że obiekt jest niemutowalny.

Nie rozumiem za bardzo jednak jak fluent builder rozwiązuje ten drugi problem.

Możesz sobie przeprowadzić weryfikacje pól kiedy wołasz build i sprawdzić czy są dobrze wypełnione. W przypadku settrów nie da się tak zrobić.


"Nie brookliński most, ale przemienić w jasny, nowy dzień najsmutniejszą noc - to jest dopiero coś!"
edytowany 1x, ostatnio: Shalom
W0
  • Rejestracja:ponad 12 lat
  • Ostatnio:około godziny
  • Postów:3539
0

@paranoise: rozróżnijmy dwie kwestie:

  1. Wzorzec Builder.
  2. Fluent API.

Builder powstał po to, żeby przy np. przy N polach nie trzeba było tworzyć od cholery kostruktorów. O ile w obiektach mutowalnych to dosyć łatwo obejść, o tyle w obiektach niemutowalnych już trudniej.
Fluent API z kolei służy temu, aby człowiek tworzący coś nie pominął żadnego wymagalnego kroku.

I teraz - FluentBuilder to taki dziwny twór, który jednocześnie zapewnia niemutowalność, oraz to, że na etapie tworzenia obiektu programista nie zapomni o jakimś polu. Takie połączenie dwóch wzorców z definicji będzie wymagało dużo kodu i nie zawsze będzie warte zachodu. W podanym przez ciebie przykładzie taki FluentBuilder nawet nie ma zbytnio sensu, bo w każde pole można wrzucić cokolwiek - i wynikowy Email może nie mieć sensu.

FluentBuildera raczej nie używałbym do budowy POJO, tylko do konstrukcji jakichś bardziej skomplikowanych rzeczy.

Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)