Modelowanie Kawiarki - Wzorzec Builder

0

Witajcie!
Mam za zadanie zamodelować kawiarkę używając wzorca Builder.
Dokładna treść zadania: https://prnt.sc/vfoer9 - screenshot-20201108195050.png
Nie wiem czy może powinienem skopiować tutaj całą treść ale wydaje mi się, że na obrazie łatwiej będzie to "zrozumieć", bo są kolorki użyte.

Zadanie jest pracą domową na studiach, wiem że nie każdy lubi pomagać jeśli o nie chodzi ale liczę na jakikolwiek odzew - poradę / krytykę.

Jest to moje pierwsze doświadczenie z jakimkolwiek wzorcem projektowym, getterami i setterami i typem enum (znam enum w języku C).

Nie wiem czy to co robię jest jakkolwiek poprawne, a nawet jeśli tak to nie wiem jak dalej ugryźć to zadanie.
Proszę przejrzycie kod poniżej i dajcie wskazówki. Czas mam do wtorku więc już z desperacji musiałem kogoś poprosić o pomoc, bo nie poradziłem sobie z nim sam. (Zadane zostało w piątek).
//Plik 1 - MokaPot.java - metody (settery / gettery) do tworzenia kawiarki
//Plik 2 - Main.java - tworzenie kawiarki za pomocą łańcuchowego wywołania metod


public class MokaPot  {
    public enum SupportedCookers {
        Electric, Stove, Induction;
    }
    private Integer setNoCups ;
    private String setMaterial;
    private String setMaker;
    private Double setPrice;
    private boolean setReducer;
    private boolean setMilkFrother;
    private SupportedCookers setCookerType;
    public static Builder builder() {
        return new Builder();
    }
    private MokaPot() {}

    public void setCookerType(SupportedCookers setCookerType) {
        this.setCookerType = setCookerType;
    }
    public SupportedCookers getSupportedCookers() {
        return this.setCookerType;
    }
    public static final class Builder {
        private Integer setNoCups=1;
        private String setMaterial;
        private String setMaker = "MokaPot";
        private Double setPrice;
        private boolean setReducer = false;
        private boolean setMilkFrother=false;
        public Builder setNoCups (Integer setNoCups ) {
            this.setNoCups  = setNoCups;
            return this;
        }
        public Builder setMaterial (String setMaterial) {
            this.setMaterial  = setMaterial;
            return this;
        }
        public Builder setMaker (String setMaker ) {
            this.setMaker  = setMaker ;
            return this;
        }


        public Builder setPrice (Double setPrice) {
            this.setPrice  = setPrice;
            return this;
        }

        public Builder setReducer  (boolean setReducer) {
            this.setReducer  = setReducer;
            return this;
        }
        public Builder setMilkFrother (Boolean setMilkFrother ) {
            this.setMilkFrother  = setMilkFrother;
            return this;
        }
        public MokaPot build() {
            if(setNoCups == null){
                throw new IllegalStateException("Cups count can't be empty");
            }

            //ten kod był w przykladzie uzycia wzorca builder na stronie:
            //https://devcave.pl/effective-java/wzorzec-projektowy-builder
            MokaPot mokaPot = new MokaPot();
            mokaPot.setNoCups = this.setNoCups;
            mokaPot.setMaterial = this.setMaterial;
            mokaPot.setMaker = this.setMaker;
            mokaPot.setPrice = this.setPrice;
            mokaPot.setReducer = this.setReducer;
            mokaPot.setMilkFrother = this.setMilkFrother;
            //tu sprawdzalem sobie czy to jakkolwiek działa
            System.out.println(mokaPot.setNoCups);
            System.out.println(mokaPot.setMaterial);
            System.out.println(mokaPot.setMaker);
            System.out.println(mokaPot.setPrice);
            System.out.println(mokaPot.setReducer);
            System.out.println(mokaPot.setMilkFrother);
            return mokaPot;
        }


    }
}



public class Main {

    public static void main(String[] args) {
        MokaPot  mokaPot1 = MokaPot.builder()
                .setMilkFrother(true)
                .setReducer(false)
                .setMaker("Bialetti")
                .build();
        System.out.println("-------------------");
        MokaPot  mokaPot2 = MokaPot.builder()
                .setMilkFrother(true)
                .setReducer(false)
                .setMaker("Bialetti")
                .build();
        System.out.println("-------------------");
        //sprawdzam czy kawiarki sa takie same
        System.out.println(mokaPot1.equals(mokaPot2));
        //z jakiegos powodu pokazuje "false"  ## :( ##
    }
}

Jak widać zrobiłem "coś" lecz w tym wszystkim się nie mogę odnaleźć.
Może na koniec powinienem zadać jakieś pytanie żebyście wiedzieli jakiej pomocy bym chciał lecz na wstępie chyba po prostu potrzebuję żeby ktoś z doświadczeniem spojrzał na to cudo.

2

Implementacja wzorca wyglada niezle, problem jest z ta klasa której instancje budujesz, czyli pare wykładów wcześniej :)

  1. Kosmetyka: Dziwne nazwy pól klasy setXXX. Po to robisz buildera, żeby nie mieć setterów.
  2. Nie nadpisales equalsa, wiec 2 różne instancje zawsze będą... różne.
0

@Charles_Ray:
Bardzo dziękuję

  1. dziwne nazwy chyba muszę niestety użyć, bo tak zostały nazwane w zadaniu. Może się to trochę kłóci z ogólnie przyjętymi zasadami ale no wolę nie odbiegać od zadania. Chyba, że mój kod powinien zupełnie inaczej wyglądać i rozdzielić jakoś builder i settery ale to by chyba miało jeszcze mniejszy sens.

  2. Racja! Nie wiem czy dobrze myślę czy nie ale zrobiłem kod w którym po prostu są porównywane każde pola i jeśli wszystko jest takie samo to equals zwraca true.
    Jednak mam problem z kompilacją gdyż nie mogę zrobić "@Override".
    Error:(94, 9) java: method does not override or implement a method from a supertype

package com.company;

public class MokaPot {
    public enum SupportedCookers {
        Electric, Stove, Induction;
    }

    private Integer setNoCups;
    private String setMaterial;
    private String setMaker;
    private Double setPrice;
    private boolean setReducer;
    private boolean setMilkFrother;
    private SupportedCookers setCookerType;

    public static Builder builder() {
        return new Builder();
    }

    private MokaPot() {
    }

    public void setCookerType(SupportedCookers setCookerType) {
        this.setCookerType = setCookerType;
    }

    public SupportedCookers getSupportedCookers() {
        return this.setCookerType;
    }

    public static final class Builder {
        private Integer setNoCups = 1;
        private String setMaterial;
        private String setMaker = "MokaPot";
        private Double setPrice;
        private boolean setReducer = false;
        private boolean setMilkFrother = false;

        public Builder setNoCups(Integer setNoCups) {
            this.setNoCups = setNoCups;
            return this;
        }

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

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


        public Builder setPrice(Double setPrice) {
            this.setPrice = setPrice;
            return this;
        }

        public Builder setReducer(boolean setReducer) {
            this.setReducer = setReducer;
            return this;
        }

        public Builder setMilkFrother(Boolean setMilkFrother) {
            this.setMilkFrother = setMilkFrother;
            return this;
        }

        public MokaPot build() {
            if (setNoCups == null) {
                throw new IllegalStateException("Cups count can't be empty");
            }

            //ten kod był w przykladzie uzycia wzorca builder na stronie:
            //https://devcave.pl/effective-java/wzorzec-projektowy-builder
            MokaPot mokaPot = new MokaPot();
            mokaPot.setNoCups = this.setNoCups;
            mokaPot.setMaterial = this.setMaterial;
            mokaPot.setMaker = this.setMaker;
            mokaPot.setPrice = this.setPrice;
            mokaPot.setReducer = this.setReducer;
            mokaPot.setMilkFrother = this.setMilkFrother;
            //tu sprawdzalem sobie czy to jakkolwiek działa
            System.out.println(mokaPot.setNoCups);
            System.out.println(mokaPot.setMaterial);
            System.out.println(mokaPot.setMaker);
            System.out.println(mokaPot.setPrice);
            System.out.println(mokaPot.setReducer);
            System.out.println(mokaPot.setMilkFrother);
            return mokaPot;
        }

        @Override
        public boolean equals(MokaPot mokaObject) {

            // If the object is compared with itself then return true
            if (this.setNoCups == mokaObject.setNoCups &&
                    this.setMaterial == mokaObject.setMaterial &&
                    this.setMaker == mokaObject.setMaker &&
                    this.setPrice == mokaObject.setPrice &&
                    this.setReducer == mokaObject.setReducer &&
                    this.setMilkFrother == mokaObject.setMilkFrother) {
                return true;
            } else
                return false;
        }

    }

}

Jeśli chodziło o zupełnie coś innego w nadpisaniu equals to prosiłbym o dodatkową wskazówkę.

##EDIT:
Intellij (IDE) podpowiedziało by zrobić nowy interface Builder, a do niego wykonać pull metody equals. Dzięki temu udaje się skompilować kod.

public interface Builder {
    boolean equals(MokaPot mokaObject);
}

Jednak niestety działanie equals nadal zwraca false po nadpisaniu w ten sposób

##Edit 2:
Dzięki @crejk udało się zrobić @Override by się kompilowało. Kod wstawiam poniżej.

2

Bo nadpisałeś equals w klasie Builder, a nie w klasie MokaPot btw. klikasz prawym generate equals and hashCode

0

@crejk: Dziękuję jeszcze raz za pomysł z generacją equals and hashcode.
Również poprawiłem nazwy pól i tylko nazwy metod mają w nazwie set. Już teraz widzę jaka to była głupota :D

Obecny kod MokaPot.java poniżej.

package com.company;

import java.util.Objects;

public class MokaPot {
    public enum SupportedCookers {
        Electric, Stove, Induction;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        MokaPot mokaPot = (MokaPot) o;
        return Reducer == mokaPot.Reducer &&
                MilkFrother == mokaPot.MilkFrother &&
                NoCups.equals(mokaPot.NoCups) &&
                Objects.equals(Material, mokaPot.Material) &&
                Objects.equals(Maker, mokaPot.Maker) &&
                Objects.equals(Price, mokaPot.Price) &&
                CookerType == mokaPot.CookerType;
    }

    @Override
    public int hashCode() {
        return Objects.hash(NoCups, Material, Maker, Price, Reducer, MilkFrother, CookerType);
    }


    private Integer NoCups;
    private String Material;
    private String Maker;
    private Double Price;
    private boolean Reducer;
    private boolean MilkFrother;
    private SupportedCookers CookerType;

    public static Builder builder() {
        return new Builder();
    }

    private MokaPot() {
    }

    public void setCookerType(SupportedCookers CookerType) {
        this.CookerType = CookerType;
    }

    public SupportedCookers getSupportedCookers() {
        return this.CookerType;
    }

    public static final class Builder {
        private Integer NoCups = 1;
        private String Material;
        private String Maker = "MokaPot";
        private Double Price;
        private boolean Reducer = false;
        private boolean MilkFrother = false;

        public Builder setNoCups(Integer NoCups) {
            this.NoCups = NoCups;
            return this;
        }

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

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

        public Builder setPrice(Double Price) {
            this.Price = Price;
            return this;
        }

        public Builder setReducer(boolean Reducer) {
            this.Reducer = Reducer;
            return this;
        }

        public Builder setMilkFrother(Boolean MilkFrother) {
            this.MilkFrother = MilkFrother;
            return this;
        }

        public MokaPot build() {
            if (NoCups == null) {
                throw new IllegalStateException("Cups count can't be empty");
            }

            //ten kod był w przykladzie uzycia wzorca builder na stronie:
            //https://devcave.pl/effective-java/wzorzec-projektowy-builder
            MokaPot mokaPot = new MokaPot();
            mokaPot.NoCups = this.NoCups;
            mokaPot.Material = this.Material;
            mokaPot.Maker = this.Maker;
            mokaPot.Price = this.Price;
            mokaPot.Reducer = this.Reducer;
            mokaPot.MilkFrother = this.MilkFrother;
            //tu sprawdzalem sobie czy to jakkolwiek działa
            System.out.println(mokaPot.NoCups);
            System.out.println(mokaPot.Material);
            System.out.println(mokaPot.Maker);
            System.out.println(mokaPot.Price);
            System.out.println(mokaPot.Reducer);
            System.out.println(mokaPot.MilkFrother);
            return mokaPot;
        }



    }

}

Prosiłbym o kolejne wskazówki.
W zadaniu jest napisane "Dodaj też gettera: Iterable<...> getSupportedCookers()." - w mojej implementacji mam już gettera ale bez "Iterable". Nie bardzo wiem po co jest to w treści zadania ani jak to zaimplementować.

Obiekty klasy kawiarka mają być „używalne” w kolekcjach haszowalnych (wskazówka: equals, hashCode), powinny też wypisywać o sobie informację (toString). Klasa kawiarki ma też implementować Comparable (porównywanie po cenie).
Czyli jak rozumiem oznacza to, że muszę @overridde dla toString() w klasie MokaPot + do klasy MokaPot dodać dopisek implements Comparable, a w klasie dodać @Override do compare() ?

##EDIT:
Obecny kod według tego co myślałem, że trzeba zrobić.

package com.company;

import java.util.Objects;

public class MokaPot implements Comparable<MokaPot> {
    @Override
    public int compareTo(MokaPot o) {
        int isBigger = this.Price.compareTo(o.Price);
        return isBigger == 0 ? this.Price.compareTo(o.Price) : isBigger;
    }

    public enum SupportedCookers {
        Electric, Stove, Induction;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        MokaPot mokaPot = (MokaPot) o;
        return Reducer == mokaPot.Reducer &&
                MilkFrother == mokaPot.MilkFrother &&
                NoCups.equals(mokaPot.NoCups) &&
                Objects.equals(Material, mokaPot.Material) &&
                Objects.equals(Maker, mokaPot.Maker) &&
                Objects.equals(Price, mokaPot.Price) &&
                CookerType == mokaPot.CookerType;
    }

    @Override
    public int hashCode() {
        return Objects.hash(NoCups, Material, Maker, Price, Reducer, MilkFrother, CookerType);
    }

    @Override
    public String toString() {
        return String.format("\nCups Amount: " + NoCups +
                "\nMaterial: " + Material +
                "\nMaker: " + Maker +
                "\nPrice: " + Price +
                "\nReducer: " + Reducer +
                "\nMilkFrother: " + MilkFrother +
                "\nCooker Type: " + CookerType);
    }

    private Integer NoCups;
    private String Material;
    private String Maker;
    private Double Price;
    private boolean Reducer;
    private boolean MilkFrother;
    private SupportedCookers CookerType;

    public static Builder builder() {
        return new Builder();
    }

    private MokaPot() {
    }

    public void setCookerType(SupportedCookers CookerType) {
        this.CookerType = CookerType;
    }

    public SupportedCookers getSupportedCookers() {
        return this.CookerType;
    }

    public static final class Builder {
        private Integer NoCups = 1;
        private String Material;
        private String Maker = "MokaPot";
        private Double Price;
        private boolean Reducer = false;
        private boolean MilkFrother = false;

        public Builder setNoCups(Integer NoCups) {
            this.NoCups = NoCups;
            return this;
        }

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

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

        public Builder setPrice(Double Price) {
            this.Price = Price;
            return this;
        }

        public Builder setReducer(boolean Reducer) {
            this.Reducer = Reducer;
            return this;
        }

        public Builder setMilkFrother(Boolean MilkFrother) {
            this.MilkFrother = MilkFrother;
            return this;
        }

        public MokaPot build() {
            if (NoCups == null) {
                throw new IllegalStateException("Cups count can't be empty");
            }

            //ten kod był w przykladzie uzycia wzorca builder na stronie:
            //https://devcave.pl/effective-java/wzorzec-projektowy-builder
            MokaPot mokaPot = new MokaPot();
            mokaPot.NoCups = this.NoCups;
            mokaPot.Material = this.Material;
            mokaPot.Maker = this.Maker;
            mokaPot.Price = this.Price;
            mokaPot.Reducer = this.Reducer;
            mokaPot.MilkFrother = this.MilkFrother;
            //tu sprawdzalem sobie czy to jakkolwiek działa
            System.out.println(mokaPot.NoCups);
            System.out.println(mokaPot.Material);
            System.out.println(mokaPot.Maker);
            System.out.println(mokaPot.Price);
            System.out.println(mokaPot.Reducer);
            System.out.println(mokaPot.MilkFrother);
            return mokaPot;
        }


    }

}

Prosze o komentarz czy to tak ma być właśnie. Jeśli się zgadza, to pozostaje:

Uwaga: jeśli nie została dla obiektu wywołana metoda build(), to toString powinno informować, że obiekt nie został zbudowany (ukończony), zaś compareTo(...) ma rzucać stosowny wyjątek (jeśli obiekt bieżący lub przekazany do porównania nie jest ukończony).

Tu prosiłbym o wsparcie, bo nie bardzo wiem jak to zaimplementować i gdzie. Czy to w if'ie jakimś przy tworzeniu obiektu MokaPot w Main czy w metodzie build() jakoś?

2
  1. Wywal te settery / ustawianie pól "na pałe" i zrób konstuktor ze wszystkimi parametrami który wywołasz z build()
  2. Zrób wszystkie pola w tej twojej klasie final.

Ad twojego ostatniego pytania->

  1. Masz zrobić toString w klasie Buildera
  2. Masz zrobić takie equals w twojej głownej klasie, że sprawdza czy aby czasem ktos nie przekazał Buildera zamiast samego obiektu i rzucic wyjątek
1
Polskie hacki napisał(a):

W zadaniu jest napisane "Dodaj też gettera: Iterable<...> getSupportedCookers()." - w mojej implementacji mam już gettera ale bez "Iterable". Nie bardzo wiem po co jest to w treści zadania ani jak to zaimplementować.

Oznacza to, że supportedCookers to powinna być jakaś kolekcja, a nie pojedyńczy typ.

0

@Shalom:

  1. W treści zadania jest napisane " klasa kawiarki ma zawierać metody typu " więc chyba twój pomysł z modyfikowaniem konstruktora niestety może doktor uznać za błędny
  2. Słuszna uwaga. Bardzo dziękuję. Również nazwy pól zmienię na pisane z małych liter, bo o tym zapomniałem.
    Co do reszty to bardzo dziękuję. Niestety nie mogę dalej wpaść na to co te metody mają w sobie zawierać.
  3. toString - rozumiem, że tu będzie jakiś if() sprawdzający czy metoda build została użyta jednak nie mam pojęcia jak takie coś napisać w kodzie (sprawdzenie użycia metody). Dodatkowo, nie rozumiem zbytnio co w klasie Builder powinienem dodać by taki toString się wykonał i dał nam znać czy metoda została użyta.
  4. Mówisz by zrobić metodę equals, a w treści jest napisane o metodzie toString i compareTo. Mógłbym Cię prosić o potwierdzenie, że właśnie chodzi o metodę equals i dlaczego tak?
    I również prosiłbym o sprawdzenie czy chodzi o taki kod:

@Override
    public boolean equals(Object o) {
        if (o == null || getClass() != o.getClass()) throw new IllegalArgumentException("Obiekty pochodza z tej samej klasy");
        return false;
    }

Bo jeśli dobrze rozumiem to właśnie to odpowiada za sprawdzenie czy czasem obiekty nie pochodzą z tej samej klasy.
Ale nie wiem czy powinno być return false; na końcu i czy cokolwiek jeszcze w tej metodzie miałbym dopisać ale wydaje mi się, że tylko o to chodzi, tak?

@crejk:
Czyli po prostu chodzi o to żeby wykorzystać enum tak? Czy jeszcze o inną kolekcje chodzi?

1

toString - rozumiem, że tu będzie jakiś if() sprawdzający czy metoda build została użyta jednak nie mam pojęcia jak takie coś napisać w kodzie (sprawdzenie użycia metody). Dodatkowo, nie rozumiem zbytnio co w klasie Builder powinienem dodać by taki toString się wykonał i dał nam znać czy metoda została użyta.

Co? Nie bardzo rozumiem o_O Ten toString niech zawsze to wypisuje dla obiektu Builder. Ja myśle że chodzi tu o problem z var. Wyobraź sobie że masz kod:

var kawiarka = MokaPot.builder()
                .setMilkFrother(true)
                .setReducer(false)
                .setMaker("Bialetti");

I zapomnisz o tym że to jest jeszcze builder a nie sam obiekt!

Mówisz by zrobić metodę equals, a w treści jest napisane o metodzie toString i compareTo. Mógłbym Cię prosić o potwierdzenie, że właśnie chodzi o metodę equals i dlaczego tak?

No jak masz zmienic tylko compareTo to zmień tylko compareTo :)

W treści zadania jest napisane " klasa kawiarki ma zawierać metody typu " więc chyba twój pomysł z modyfikowaniem konstruktora niestety może doktor uznać za błędny

Ale przecież teraz jak zawołasz setter na tym obiekcie to ci zmieni wartość pola a ma to nie być możliwe! I co teraz? :) Albo nie masz setterów wcale (jak w której dalszej iteracji twojego kodu). Nie wiem co twój prowadzący miał na myśli, ale wiem jak normalnie implementuje sie buildera i robi sie to właśnie przez:

  • wszystkie pola są final (żeby było immutable)
  • prywatny konstruktor ze wszystkimi polami
1
Polskie hacki napisał(a):

@crejk:
Czyli po prostu chodzi o to żeby wykorzystać enum tak? Czy jeszcze o inną kolekcje chodzi?

Enum to nie kolekcja. https://docs.oracle.com/javase/8/docs/api/java/util/Collection.html

0

Próbowałem przez godzinę cokolwiek ruszyć i nic mi się nie udało.

Mogę prosić o przybliżenie mi tych rzeczy? Do tej pory były mi one obce i ciężko coś zaimplementować.

  1. Sprawa z Enum i Iterable<...> getSupportedCookers()
    Próbowałem dodać dopisek, że klasa extends Iterable<MokaPot>. Potem pomyślałem, że może by zrobić override metody forEach tak by getSupportedCookers właśnie wypisało mi wszystkie opcje z Enum'a za pomocą tej funkcji. Wyczytałem, że do tego też będę potrzebował najpierw wszystkie "obiekty" (czy jakkolwiek się je nazywa) dodać do ArrayList, a następnie ją iterować by wypisała wszystkie opcje.

Niestety implementacja tego totalnie mi nie wyszła. @crejk: myślisz, że o to właśnie chodziło w tym zadaniu, czy źle do tego podchodzę?

  1. Powracam z pytaniem w jaki sposób override metody toString() w klasie Builder może poinformować mnie ze na obiekcie nie zostala uzyta metoda build?
    Obecnie bez wywołania metody build() wyskakuje error:
    Error:(14, 26) java: incompatible types: com.company.MokaPot.Builder cannot be converted to com.company.MokaPot

  2. Gdy próbówałem zrobić pola na finalne - wyskakiwał błąd:
    Error:(122, 20) java: cannot assign a value to final variable noCups - ponieważ w metodzie build() te wartości są przypisywane do tych pól obiektu. Nie wymyśliłem sposobu na zrobienie obejścia do tego.

Obecny plik MokaPot.java

package com.company;
import java.util.Objects;

public class MokaPot implements Comparable<MokaPot> {
    private Integer noCups;
    private String material;
    private String maker;
    private Double price;
    private boolean reducer;
    private boolean milkFrother;
    private SupportedCookers cookerType;

    @Override
    public int compareTo(MokaPot o) {
        int isBigger = this.price.compareTo(o.price);
        return isBigger == 0 ? this.price.compareTo(o.price) : isBigger;
    }


    public enum SupportedCookers {
        ELECTRIC, CERAMIC, INDUCTION, GAS;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        MokaPot mokaPot = (MokaPot) o;
        return reducer == mokaPot.reducer &&
                milkFrother == mokaPot.milkFrother &&
                noCups.equals(mokaPot.noCups) &&
                Objects.equals(material, mokaPot.material) &&
                Objects.equals(maker, mokaPot.maker) &&
                Objects.equals(price, mokaPot.price) &&
                cookerType == mokaPot.cookerType;
    }

    @Override
    public int hashCode() {
        return Objects.hash(noCups, material, maker, price, reducer, milkFrother, cookerType);
    }

    @Override
    public String toString() {
        return String.format("\nCups Amount: " + noCups +
                "\nMaterial: " + material +
                "\nMaker: " + maker +
                "\nPrice: " + price +
                "\nReducer: " + reducer +
                "\nMilkFrother: " + milkFrother +
                "\nCooker Type: " + cookerType);
    }


    public static Builder builder() {
        return new Builder();
    }

    private MokaPot() {
    }

    public void setCookerType(SupportedCookers cookerType) {
        this.cookerType = cookerType;
    }
    public SupportedCookers getSupportedCookers() {
        return this.cookerType;
    }

    public static final class Builder {
        @Override
        public String toString() {
            return String.format("Obiekt zbudowany pomyślnie");
        }

        private Integer noCups = 1;
        private String material;
        private String maker = "MokaPot";
        private Double price;
        private boolean reducer = false;
        private boolean milkFrother = false;

        public Builder setNoCups(Integer noCups) {
            this.noCups = noCups;
            return this;
        }

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

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

        public Builder setPrice(Double price) {
            this.price = price;
            return this;
        }

        public Builder setReducer(boolean reducer) {
            this.reducer = reducer;
            return this;
        }

        public Builder setMilkFrother(Boolean milkFrother) {
            this.milkFrother = milkFrother;
            return this;
        }

        public MokaPot build() {
            if (noCups == null) {
                throw new IllegalStateException("Cups count can't be empty");
            }

            MokaPot mokaPot = new MokaPot();
            mokaPot.noCups = this.noCups;
            mokaPot.material = this.material;
            mokaPot.maker = this.maker;
            mokaPot.price = this.price;
            mokaPot.reducer = this.reducer;
            mokaPot.milkFrother = this.milkFrother;

            return mokaPot;
        }

    }

}

1
Polskie hacki napisał(a):

Próbowałem przez godzinę cokolwiek ruszyć i nic mi się nie udało.

Mogę prosić o przybliżenie mi tych rzeczy? Do tej pory były mi one obce i ciężko coś zaimplementować.

  1. Sprawa z Enum i Iterable<...> getSupportedCookers()
    Próbowałem dodać dopisek, że klasa extends Iterable<MokaPot>. Potem pomyślałem, że może by zrobić override metody forEach tak by getSupportedCookers właśnie wypisało mi wszystkie opcje z Enum'a za pomocą tej funkcji. Wyczytałem, że do tego też będę potrzebował najpierw wszystkie "obiekty" (czy jakkolwiek się je nazywa) dodać do ArrayList, a następnie ją iterować by wypisała wszystkie opcje.

Niestety implementacja tego totalnie mi nie wyszła. @crejk: myślisz, że o to właśnie chodziło w tym zadaniu, czy źle do tego podchodzę?

powinno to wyglądać mniej więcej tak:

public class MokaPot  {

    private final List<Cooker> supportedCookers;

    public MokaPot(List<Cooker> supportedCookers) { // przekazujemy kolekcje przez konstruktor w tym przypadku listę wspieranych kuchenek. Możemy przykładowo przekazać Arrays.asList(Cooker.Electric, Cooker.Stove);
        this.supportedCookers = supportedCookers;
    }

    public Iterable<Cooker> getSupportedCookers() {
        return supportedCookers; 
    }
}
Polskie hacki napisał(a):

Powracam z pytaniem w jaki sposób override metody toString() w klasie Builder może poinformować mnie ze na obiekcie nie zostala uzyta metoda build?
Obecnie bez wywołania metody build() wyskakuje error:
Error:(14, 26) java: incompatible types: com.company.MokaPot.Builder cannot be converted to com.company.MokaPot

Jeśli nadal jesteś w klasie Builder to nie została użyta metoda build.

Polskie hacki napisał(a):

Gdy próbówałem zrobić pola na finalne - wyskakiwał błąd:
Error:(122, 20) java: cannot assign a value to final variable noCups - ponieważ w metodzie build() te wartości są przypisywane do tych pól obiektu. Nie wymyśliłem sposobu na zrobienie obejścia do tego.

Jeśli masz pola finalne musisz stworzyć odpowiedni konstruktor tak jak pokazałem w kodzie wyżej.
Wtedy w metodzie build nie robisz już tak jak wcześniej tylko:

public MokaPot build() {
    return new MokaPot(this.noCups, this.material, this.price, itd);
}
0

@crejk: Ewidentnie coś robię nie tak w implementacji :(
Po walce z wieloma błędami udało mi się ustawić w końcu pola na finalne.
Nie wiem dlaczego getSupportedCookers wypisuje mi null (w funkcji @Override toString w MokaPot próbowałem z użyciem samej funkcji gettera i po prostu supportedCookers. Oba zwracają null.

Kompilator nie zezwalał mi na zrobienie @Override w klasie Builder. Error: "method does not override or implement a method from a supertype"
IDE podpowiedziało żeby stworzyć nowy Interface Builder. Okej może się kompiluje ale nadal nic się nie wyświetla w trakcie użycia funkcji build(). Powinienem gdzieś dodać System.out.Println ale nie mam pojęcia gdzie. Prototyp funkcji jakiś mam ale żeby się tym bardziej pobawić to nie było jak więc pewnie jest do poprawy.

Walczyłem dosyć sporo z konstruktorami i podejrzewam, że jest tam źle ustawione to SupportedCookers. W MokaPot build() też wymagało dodać do return'a supportedCookers ale intuicja podpowiada mi, że to źle wyszło.

Kod:

Main.java


package com.company;

public class Main implements Comparable<MokaPot> {

    public static void main(String[] args) {
        MokaPot mokaPot1 = MokaPot.builder()
                .setMilkFrother(true)
                .setReducer(false)
                .setMaker("Bialetti")
                .build();

        MokaPot mokaPot2 = MokaPot.builder()
                .setMilkFrother(true)
                .setReducer(false)
                .setMaker("Bialetti")
                .build();


        System.out.println(mokaPot1.equals(mokaPot2)); //sprawdzam czy kawiarki sa takie same
        System.out.println(mokaPot1); //wypis informacji o modelu kawiarki
    }

    @Override
    public int compareTo(MokaPot o) {
        System.out.println("this.getclass = " + this.getClass());
        System.out.println("o.getClass().getComponentType() = " + o.getClass().getComponentType());
        if (o == null || this.getClass() != o.getClass().getComponentType())
            throw new IllegalArgumentException("Obiekty pochodza z tej samej klasy");
        return 0;
    }
}

MokaPot.java


package com.company;

import java.util.List;
import java.util.Objects;

public class MokaPot implements Comparable<MokaPot> {
    private final Integer noCups;
    private String material;
    private String maker;
    private Double price;
    private boolean reducer;
    private boolean milkFrother;


    @Override
    public int compareTo(MokaPot o) {
        int isBigger = this.price.compareTo(o.price);
        return isBigger == 0 ? this.price.compareTo(o.price) : isBigger;
    }

    private static List<SupportedCookers> supportedCookers;

    public MokaPot(Integer noCups, String material, String maker, Double price, boolean reducer, boolean milkFrother, List<SupportedCookers> supportedCookers) { // przekazujemy kolekcje przez konstruktor w tym przypadku listę wspieranych kuchenek. Możemy przykładowo przekazać Arrays.asList(Cooker.Electric, Cooker.Stove);
        this.supportedCookers = supportedCookers;
        this.noCups = noCups;
        this.material = material;
        this.maker = maker;
        this.price = price;
        this.reducer = reducer;
        this.milkFrother = milkFrother;
    }

    public Iterable<SupportedCookers> getSupportedCookers() {
        return supportedCookers;
    }

    public enum SupportedCookers {
        ELECTRIC, CERAMIC, INDUCTION, GAS;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        MokaPot mokaPot = (MokaPot) o;
        return reducer == mokaPot.reducer &&
                milkFrother == mokaPot.milkFrother &&
                noCups.equals(mokaPot.noCups) &&
                Objects.equals(material, mokaPot.material) &&
                Objects.equals(maker, mokaPot.maker) &&
                Objects.equals(price, mokaPot.price) &&
                supportedCookers == mokaPot.supportedCookers;
    }

    @Override
    public int hashCode() {
        return Objects.hash(noCups, material, maker, price, reducer, milkFrother, supportedCookers);
    }

    @Override
    public String toString() {
        return String.format("\nCups Amount: " + noCups +
                "\nMaterial: " + material +
                "\nMaker: " + maker +
                "\nPrice: " + price +
                "\nReducer: " + reducer +
                "\nMilkFrother: " + milkFrother +
                "\nCooker Type: " + supportedCookers);
    }


    public static Builder builder() {
        return new Builder();
    }


    public static final class Builder implements com.company.Builder {
        @Override
        public String toString(Object o) {
            if (o == null || this.getClass().getName() == o.getClass().getName())
                return String.format("Metoda build() nie została użyta");
            else
                return String.format("Obiekt został zbudowany pomyślnie");
        }

        private Integer noCups = 1;
        private String material;
        private String maker = "MokaPot";
        private Double price;
        private boolean reducer = false;
        private boolean milkFrother = false;

        public Builder setNoCups(Integer noCups) {
            this.noCups = noCups;
            return this;
        }

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

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

        public Builder setPrice(Double price) {
            this.price = price;
            return this;
        }

        public Builder setReducer(boolean reducer) {
            this.reducer = reducer;
            return this;
        }

        public Builder setMilkFrother(Boolean milkFrother) {
            this.milkFrother = milkFrother;
            return this;
        }


        public MokaPot build() {
            if (noCups == null) {
                throw new IllegalStateException("Cups count can't be empty");
            }

            return new MokaPot(this.noCups, this.material, this.maker, this.price, this.reducer, this.milkFrother, supportedCookers);

        }

    }

}

Builder.java


package com.company;

public interface Builder {
    String toString(Object o);
}

1

Nie masz w ogóle pola supportedCookers w builderze.
Musisz zrobić przykładowo:

    class Builder {

    private final List<Cooker> supportedCookers = new ArrayList<>();

    public Builder addSupportedCooker(Cooker cooker) {
        this.supportedCookers.add(cooker);
        return this;
    }

    public MokaPot build() {
        return new MokaPot(this.param1, this.param2, this.supportedCookers)
    }

Za pomocą metody addSupportedCooker możesz teraz dodawać w builderze wspierane kuchenki.
W metodzie build przekazujesz tą liste do konstruktora.

metoda toString() nie może zawierać żadnych parametrów. Ten interface Builder nie ma żadnego sensu, IDE ci tak podpowiada, ponieważ użyłeś adnotacji @Override, która po prostu sygnalizuje, że twoja metoda jest implementowana z interfejsu.
supportedCookers == mokaPot.supportedCookers Nie powinno się tak robić, wygeneruj hashCode i equals od nowa.

0
crejk napisał(a):

Jeśli nadal jesteś w klasie Builder to nie została użyta metoda build.

Jeśli dobrze zrozumiałem to można uzyskać to takim sposobem.


@Override
        public String toString() {
            if (this.getClass().getName() == "Builder")
                return String.format("Metoda build() nie została użyta");
            else
                return String.format("Kawiarka została zbudowana pomyślnie");
        }

A teraz by to sprawdzić myślałem, że muszę gdzieś (chyba w klasie Main) dodać System.out.println(//Builder.xxx). Próbowałem różne kombinacje i nic z tego więc potrzebuję pomocy. Jest to już chyba ostatnia rzecz do zrobienia w tym zadaniu za co wszystkim pomagającym bardzo dziękuję!

1
Polskie hacki napisał(a):
crejk napisał(a):

Jeśli nadal jesteś w klasie Builder to nie została użyta metoda build.

Jeśli dobrze zrozumiałem to można uzyskać to takim sposobem.


@Override
        public String toString() {
            if (this.getClass().getName() == "Builder")
                return String.format("Metoda build() nie została użyta");
            else
                return String.format("Kawiarka została zbudowana pomyślnie");
        }

A teraz by to sprawdzić myślałem, że muszę gdzieś (chyba w klasie Main) dodać System.out.println(//Builder.xxx). Próbowałem różne kombinacje i nic z tego więc potrzebuję pomocy. Jest to już chyba ostatnia rzecz do zrobienia w tym zadaniu za co wszystkim pomagającym bardzo dziękuję!

Nazwa tej klasy nigdy się nie zmieni. Jak już tak bardzo tego potrzebujesz to zostaw po prostu samo

public String toString() {
    return "Metoda build() nie została użyta";
}
1

Do klasy Builder dodaj boolean build = false;
w wywołaniu Builder.build() ustaw go na true;
w toString w Builder mozesz sprawdzić booleana build
w main sysout(mocaPot1)

1

Metodę zawsze wołasz na jakimś obiekcie. Każdy obiekt ma swoją klasę. U Ciebie są dwie klasy - MokaPot i Builder. Czyli wewnątrz toString() dobrze wiesz jaka jest klasa obiektu, na którym to zawołałeś. Jeśli chcesz wyprintować pola z instancji klasy MokaPot, to wywołaj toString() na instancji MokaPot, a nie buildera.

0

@ćk:
Zrobiłem twoją metodą lecz gdy nie zostaje użyta metoda build() wypisuje mi 'com.company.MokaPot$Builder@723279cf" w konsoli jeśli użyję var mokaPot1 = MokaPot.builder(),
a przy próbie MokaPot mokaPot1 = MokaPot.builder(). bez użycia .build() kompilator wyrzuca błąd:
Error:(12, 36) java: incompatible types: com.company.MokaPot.Builder cannot be converted to com.company.MokaPot

##Edit, dzięki @ćk udało się to ogarnąć by wszystko działało
Dodaję zaktualizowany działający kod poniżej

Kod Main.java


package com.company;

public class Main {

    public static void main(String[] args) {
        var mokaPot1 = MokaPot.builder()
                .setMilkFrother(true)
                .setReducer(false)
                .setMaker("Bialetti")
                .setMaterial("Metal")
                .setPrice(500.5)
                .addSupportedCooker(MokaPot.SupportedCookers.ELECTRIC)
                .build();

        MokaPot mokaPot2 = MokaPot.builder()
                .setMilkFrother(true)
                .setReducer(false)
                .setMaker("Bialetti")
                .setMaterial("Metal")
                .setPrice(500.5)
                .addSupportedCooker(MokaPot.SupportedCookers.ELECTRIC)
                .build();

        System.out.println("Are these Pots the same?: " + mokaPot1.equals(mokaPot2)); //sprawdzam czy kawiarki sa takie same
        System.out.println(mokaPot1); //wypis informacji o modelu kawiarki
    }
}


Kod MokaPot.java


package com.company;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

public class MokaPot implements Comparable<MokaPot> {
    private final Integer noCups;
    private String material;
    private String maker;
    private Double price;
    private boolean reducer;
    private boolean milkFrother;

    @Override
    public int compareTo(MokaPot o) {
        int isBigger = this.price.compareTo(o.price);
        return isBigger == 0 ? this.price.compareTo(o.price) : isBigger;
    }

    private static List<SupportedCookers> supportedCookers;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        MokaPot mokaPot = (MokaPot) o;
        return reducer == mokaPot.reducer &&
                milkFrother == mokaPot.milkFrother &&
                Objects.equals(noCups, mokaPot.noCups) &&
                material.equals(mokaPot.material) &&
                maker.equals(mokaPot.maker) &&
                price.equals(mokaPot.price);
    }

    @Override
    public int hashCode() {
        return Objects.hash(noCups, material, maker, price, reducer, milkFrother);
    }

    public MokaPot(Integer noCups, String material, String maker, Double price, boolean reducer, boolean milkFrother, List<SupportedCookers> supportedCookers) { // przekazujemy kolekcje przez konstruktor w tym przypadku listę wspieranych kuchenek. Możemy przykładowo przekazać Arrays.asList(Cooker.Electric, Cooker.Stove);
        this.supportedCookers = supportedCookers;
        this.noCups = noCups;
        this.material = material;
        this.maker = maker;
        this.price = price;
        this.reducer = reducer;
        this.milkFrother = milkFrother;
    }

    public Iterable<SupportedCookers> getSupportedCookers() {
        return supportedCookers;
    }

    public enum SupportedCookers {
        ELECTRIC, CERAMIC, INDUCTION, GAS;
    }

    @Override
    public String toString() {

        return String.format("\nCups Amount: " + noCups +
                "\nMaterial: " + material +
                "\nMaker: " + maker +
                "\nPrice: " + price +
                "\nReducer: " + reducer +
                "\nMilkFrother: " + milkFrother +
                "\nSupported Cooker Types: " + supportedCookers);
    }

    public static Builder builder() {
        return new Builder();
    }

    public static final class Builder {
        @Override
        public String toString() {
            return "build " + build;
        }
        private Integer noCups = 1;
        private String material;
        private String maker = "MokaPot";
        private Double price;
        private boolean reducer = false;
        private boolean milkFrother = false;
        private final List<SupportedCookers> supportedCookers = new ArrayList<>();
        public boolean build = false;

        public Builder addSupportedCooker(SupportedCookers supportedCookers) {
            this.supportedCookers.add(supportedCookers);
            return this;
        }

        public Builder setNoCups(Integer noCups) {
            this.noCups = noCups;
            return this;
        }

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

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

        public Builder xx() {
            System.out.println("x");
            return this;
        }

        public Builder setPrice(Double price) {
            this.price = price;
            return this;
        }

        public Builder setReducer(boolean reducer) {
            this.reducer = reducer;
            return this;
        }

        public Builder setMilkFrother(Boolean milkFrother) {
            this.milkFrother = milkFrother;
            return this;
        }

        public MokaPot build() {
            if (noCups == null) {
                throw new IllegalStateException("Cups count can't be empty");
            }
            build = true;
            return new MokaPot(this.noCups, this.material, this.maker, this.price, this.reducer, this.milkFrother, this.supportedCookers);

        }

    }

}

1

Coś takiego chcesz, bo nie łapię:


public class MokaPot implements Comparable<MokaPot> {
    private final Integer noCups;
    private String material;
    private String maker;
    private Double price;
    private boolean reducer;
    private boolean milkFrother;

    @Override
    public int compareTo(MokaPot o) {
        int isBigger = this.price.compareTo(o.price);
        return isBigger == 0 ? this.price.compareTo(o.price) : isBigger;
    }

    private static List<SupportedCookers> supportedCookers;

    public MokaPot(Integer noCups, String material, String maker, Double price, boolean reducer, boolean milkFrother, List<SupportedCookers> supportedCookers) { // przekazujemy kolekcje przez konstruktor w tym przypadku listę wspieranych kuchenek. Możemy przykładowo przekazać Arrays.asList(Cooker.Electric, Cooker.Stove);
        this.supportedCookers = supportedCookers;
        this.noCups = noCups;
        this.material = material;
        this.maker = maker;
        this.price = price;
        this.reducer = reducer;
        this.milkFrother = milkFrother;
    }

    public Iterable<SupportedCookers> getSupportedCookers() {
        return supportedCookers;
    }

    public enum SupportedCookers {
        ELECTRIC, CERAMIC, INDUCTION, GAS;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        MokaPot mokaPot = (MokaPot) o;
        return reducer == mokaPot.reducer &&
                milkFrother == mokaPot.milkFrother &&
                noCups.equals(mokaPot.noCups) &&
                Objects.equals(material, mokaPot.material) &&
                Objects.equals(maker, mokaPot.maker) &&
                Objects.equals(price, mokaPot.price) &&
                supportedCookers == mokaPot.supportedCookers;
    }

    @Override
    public int hashCode() {
        return Objects.hash(noCups, material, maker, price, reducer, milkFrother, supportedCookers);
    }

    @Override
    public String toString() {
        return String.format("\nCups Amount: " + noCups +
                "\nMaterial: " + material +
                "\nMaker: " + maker +
                "\nPrice: " + price +
                "\nReducer: " + reducer +
                "\nMilkFrother: " + milkFrother +
                "\nCooker Type: " + supportedCookers);
    }

    public static Builder builder() {
        return new Builder();
    }

    public static final class Builder {
        @Override
        public String toString() {
            return "build " + build;
        }

        public boolean build = false;
        private Integer noCups = 1;
        private String material;
        private String maker = "MokaPot";
        private Double price;
        private boolean reducer = false;
        private boolean milkFrother = false;

        public Builder setNoCups(Integer noCups) {
            this.noCups = noCups;
            return this;
        }

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

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

        public Builder setPrice(Double price) {
            this.price = price;
            return this;
        }

        public Builder setReducer(boolean reducer) {
            this.reducer = reducer;
            return this;
        }

        public Builder setMilkFrother(Boolean milkFrother) {
            this.milkFrother = milkFrother;
            return this;
        }

        public MokaPot build() {
        	if (noCups == null) {
                throw new IllegalStateException("Cups count can't be empty");
            }
            
            build = true;
        	
            return new MokaPot(this.noCups, this.material, this.maker, this.price, this.reducer, this.milkFrother, supportedCookers);

        }

    }

}
package bzdura;

public class Main implements Comparable<MokaPot> {

    public static void main(String[] args) {
        
 	
    	bzdura.MokaPot.Builder b = MokaPot.builder()
                .setMilkFrother(true)
                .setReducer(false)
                .setMaker("Bialetti");

    	System.out.println(b.toString());

    	bzdura.MokaPot.Builder b2 = MokaPot.builder()
                .setMilkFrother(true)
                .setReducer(false)
                .setMaker("Bialetti");

    	System.out.println(b2);
    	
    	MokaPot mp2 = b2.build();
    	System.out.println(b2);
        
        //System.out.println(mokaPot1.equals(mokaPot2)); //sprawdzam czy kawiarki sa takie same
        //System.out.println(mokaPot1); //wypis informacji o modelu kawiarki
    }

    @Override
    public int compareTo(MokaPot o) {
        System.out.println("this.getclass = " + this.getClass());
        System.out.println("o.getClass().getComponentType() = " + o.getClass().getComponentType());
        if (o == null || this.getClass() != o.getClass().getComponentType())
            throw new IllegalArgumentException("Obiekty pochodza z tej samej klasy");
        return 0;
    }
}

Wynik:
build false
build false
build true

sorry na zmienoną nazwę pakietu :)

0

Dziękuję @ćk mam nadzieję, że to rozwiązanie przejdzie u prowadzącego

Wszystkim bardzo dziękuję za pomoc. Jeśli ktoś ma jeszcze jakieś uwagi lub pomysły to do jutra godz 12 mam czas na poprawki.

1

@Polskie hacki: ta zmienna build to powinna być jak reszta private (pisałem przy szybkim piwku) i mozesz ją jakoś inaczej nazwać
powodzenia, jak zostaniesz zapytany jak ten builder działa :)

0

@ćk: Większość się podobała prowadzącemu, a że mamy ocenę punktową to odjął mi trochę za to, że implementacja toString w zadaniu miała rzucać wyjątek, a u mnie tego nie było.
Opowiedziałem o kodzie i dostałem ocenę 1,35pkt / 1,5pkt (otrzymane / możliwe do zdobycia) więc jestem bardzo zadowolony.

Jako, że była to moja pierwsza aktywność na tym forum to chciałem wam powiedzieć, że jestem bardzo pozytywnie zaskoczony odzewem. Bez waszej pomocy nie udałoby mi się zrealizować tego zadania. Jeszcze raz wszystkim dziękuję!

Zadanie i całość kodu zostaną na tym forum i jestem pewien, że przyszłe roczniki z niego skorzystają :D

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