Страницы

Поиск по вопросам

вторник, 24 декабря 2019 г.

Правильно ли реализован паттерн Builder ? [закрыт]

#java #шаблоны_проектирования


        
             
                
                    
                        
                            Закрыт. Этот вопрос не по теме. Ответы на него в данный
момент не принимаются.
                            
                        
                    
                
                            
                                
                
                        
                            
                        
                    
                        
                            Хотите улучшить этот вопрос? Update the question so it's
on-topic for Stack Overflow на русском.
                        
                        Закрыт 4 года назад.
                                                                                
           
                
        
Здравствуйте !!!
Недавно, я приступил к изучению паттернов проектирования. Один из самых первых паттернов,
который был реализован - это Builder. Какие у Вас есть замечания по данной реализации.
Заранее спасибо ! 
Исходный код:
1) Абстрактный класс BaseCarBuilder.
public abstract class BaseCarBuilder {
    public abstract void buildCategory(CategoryCar pCategoryCar);
    public abstract void buildCarcass(CarcassType pCarcassType) ;
    public abstract void buildEngine(Engine pEngine);
    public abstract void buildColorCarcass(ColorCar pColorCar) ;
    public abstract void buildDoors(int pDoors) ;
    public Car getBuiltCar() {
        return null;
    }
}

2) Класс Car.
public class Car {

    public final static int TWO_DOORS = 2;
    public final static int THREE_DOORS = 3;
    public final static int FOUR_DOORS = 4;
    public final static int FIVE_DOORS = 5;

    private int doorsCount;

    private Engine engine;
    private String carName;
    private ColorCar colorCar;
    private CarcassType carcassType;
    private CategoryCar categoryCar;

    public Car(String pCarName) {

        this.carName = pCarName;
    }

    public ColorCar getColorCar() {
        return colorCar;
    }

    public void setColorCar(ColorCar colorCar) {
        this.colorCar = colorCar;
    }

    public CarcassType getCarcassType() {
        return carcassType;
    }

    public void setCarcassType(CarcassType carcassType) {
        this.carcassType = carcassType;
    }

    public CategoryCar getCategoryCar() {
        return categoryCar;
    }

    public void setCategoryCar(CategoryCar categoryCar) {
        this.categoryCar = categoryCar;
    }

    public int getDoorsCount() {
        return doorsCount;
    }

    public void setDoorsCount(int doorsCount) {
        this.doorsCount = doorsCount;
    }

    public Engine getEngine() {
        return engine;
    }

    public void setEngine(Engine engine) {
        this.engine = engine;
    }

    public String getCarInfo() {

        String info =
                "car name = " + carName
                        + " *** category car = " + categoryCar
                        + " *** carcass tape = " + carcassType
                        + " *** engine = " + engine
                        + " *** color = " + colorCar
                        + " *** count doors = " + doorsCount;
        System.err.println("---------------------------------------------------");
        return info;
    }
}

3) Класс MersedesBenzBuilder, который наследуется от BaseCarBuilder.
public class MersedesBenzBuilder extends BaseCarBuilder {

    private Car car;

    public MersedesBenzBuilder() {

        car = new Car("Mersedes Benz");
    }

    @Override
    public void buildCategory(CategoryCar pCategoryCar) {

        car.setCategoryCar(pCategoryCar);
    }

    @Override
    public void buildCarcass(CarcassType pCarcassType) {

        car.setCarcassType(pCarcassType);
    }

    @Override
    public void buildDoors(int pDoors) {

        car.setDoorsCount(pDoors);
    }

    @Override
    public void buildEngine(Engine pEngine) {

        car.setEngine(pEngine);
    }

    @Override
    public void buildColorCarcass(ColorCar pColorCar) {

        car.setColorCar(pColorCar);
    }
}

4) Есть класс Директор (Диспетчер).
public class Director {

    public Car buildNewCar(BaseCarBuilder carBuilder, CategoryCar pCategoryCar,
                           CarcassType pCarcassType, int pDoors, Engine pEngine,
ColorCar pColorCar) {

        Car tempCar = null;

        if (carBuilder != null) {

            carBuilder.buildCategory(pCategoryCar);
            carBuilder.buildCarcass(pCarcassType);
            carBuilder.buildDoors(pDoors);
            carBuilder.buildEngine(pEngine);
            carBuilder.buildColorCarcass(pColorCar);
            tempCar = carBuilder.getBuiltCar();

        } else {

            final String nullPoint = "Yoa are not create car_builder";
            Exception exception = new NullPointerException(nullPoint);
            exception.printStackTrace();
        }
        return tempCar;
    }
}

5) Класс который запускает этот паттерн RunBuilderPattern.

public class RunBuilderPattern {

    public static void main(String[] args) {

        Car car = null;
        Director director = new Director();

        MersedesBenzBuilder mersedesBenzBuilder = new MersedesBenzBuilder();

        car = director.buildNewCar(
                mersedesBenzBuilder,
                CategoryCar.SPORT_CAR,
                CarcassType.CABRIOLET,
                Car.TWO_DOORS, Engine.PETROL,
                ColorCar.RED);

        System.out.println("CAR INFO: " + car.getCarInfo());
    }
}

6) Дополнительные классы, точнее Enum-ы:
public enum CarcassType {

    SEDAN, UNIVERSAL, CABRIOLET, HATCHBACK, COUPE, LIMOUSINE
}

public enum CategoryCar {

    CARGO, PASSENGER, BUS, SPORT_CAR
}

public enum ColorCar {

    BLACK, WHITE, RED, GREEN, BLUE, ORANGE, BROWN
}

public enum Engine {

    DIZEL, PETROL, GAZ, HUBRID
}

7) Вот что выводится в консоль:
CAR INFO: car name = Mersedes Benz *** category car = SPORT_CAR *** carcass tape
= CABRIOLET *** engine = PETROL *** color = RED *** count doors = 2

Жду Ваших замечаний, советов, ругани и брани !!!    


Ответы

Ответ 1



Всегда пожалуйста: public abstract class BaseCarBuilder { // Мне почему-то кажется, что для примера можно было обойтись двумя-тремя // методами вида buildSomething. Ваш текущий код — это один большой // boilerplate, где из-за геттеров и сеттеров теряется смысл. public abstract void buildCategory(CategoryCar pCategoryCar); // Carcass — крайне неудачный термин. См. http://goo.gl/SCMO1q public abstract void buildCarcass(CarcassType pCarcassType) ; // Параметры с именами вида pSomething не следуют naming convention. // Если бы вы, кстати, инвертировали слова в именах вида pColorCar ----> // carColor (или просто color), уже стало бы лучше и понятнее. // pCategoryCar в текущем варианте означает что-то в духе "машина с // какой-то там категорией", а используется в значении "категория машины". public abstract void buildEngine(Engine pEngine); // Опять-таки, плывет нейминг. В текущем варианте по названиям методов // получается, что ваш builder можно попросить сделать "просто carcass", а // можно "цветной carcass". Почему бы не объединить buildColorCarcass и // buildCarcass в один метод или просто сделать buildCarcass + buildColor? public abstract void buildColorCarcass(ColorCar pColorCar) ; // Эти замечания — не очень принципиальные. См. ниже. public abstract void buildDoors(int pDoors) ; public Car getBuiltCar() { return null; } public class Car { // public final static int TWENTY_ELEVEN_DOORS = 2011. Почему не enum? public final static int TWO_DOORS = 2; public final static int THREE_DOORS = 3; public final static int FOUR_DOORS = 4; public final static int FIVE_DOORS = 5; private int doorsCount; private Engine engine; private String carName; private ColorCar colorCar; private CarcassType carcassType; private CategoryCar categoryCar; public Car(String pCarName) { this.carName = pCarName; } public ColorCar getColorCar() { return colorCar; } .... public Engine getEngine() { return engine; } public void setEngine(Engine engine) { this.engine = engine; } // Погодите, а не 'toString' ли это, часом? Вы, кстати, задумались над тем, // что, если написать в *произвольном* месте кода вот это, // // [[[ // String info = car.getCarInfo(); // info = car.getCarInfo(); // info = car.getCarInfo(); // ...... // info = car.getCarInfo(); // ]]] // // то ваш stderr окажется зафлужен строчками вида "------------------"? public String getCarInfo() { String info = "car name = " + carName + " *** category car = " + categoryCar + " *** carcass tape = " + carcassType + " *** engine = " + engine + " *** color = " + colorCar + " *** count doors = " + doorsCount; System.err.println("---------------------------------------------------"); return info; } } public class MersedesBenzBuilder extends BaseCarBuilder { private Car car; public MersedesBenzBuilder() { car = new Car("Mersedes Benz"); } @Override public void buildCategory(CategoryCar pCategoryCar) { car.setCategoryCar(pCategoryCar); } .... @Override public void buildColorCarcass(ColorCar pColorCar) { car.setColorCar(pColorCar); } } public class Director { public Car buildNewCar(BaseCarBuilder carBuilder, CategoryCar pCategoryCar, CarcassType pCarcassType, int pDoors, Engine pEngine, ColorCar pColorCar) { // Вот, теперь к главному. Вы неправильно поняли Builder по Gang of // Four, и поэтому получилась чушь. При изучении паттернов один из самых // важных принципов, как бы банально это не звучало, заключается в том, // чтобы *четко* следовать тому, что написано. Давайте внимательно // посмотрим на UML-диаграмму для паттерна Builder (http://goo.gl/rX9gMN). // Сколько параметров у метода Director.construct? Один. А у вас сколько? // Сколько параметров у метода Builder.buildPart? Ноль. А у вас сколько? // А что это значит? Правильно, что вы неверно истолковали суть этого // паттерна. // В паттерне Builder важен следующий инвариант: как только объект // ConcreteBuilder создан, он более не допускает параметризации извне. // Идея заключается в том, что конкретный экземпляр Builder умеет // каким-то своим способом совершать "маленькие шажки" к созданию // объекта (buildPart), а Director умеет объединять шажки из интерфейса // Builder таким образом, чтобы получился объект. // Конкретно в вашем случае детали типа Engine.PETROL и // CategoryCar.SPORT_CAR должны были быть "зашиты" в реализации класса // MersedesBenzBuilder. А у вас получилась такая штука, что вы вместо // Builder'a сделали крайне неочевидный named constructor, который // весело перебрасывает аргументы туда-сюда. // Годную реализацию этого паттерна на Java можете посмотреть здесь — // http://goo.gl/KRk1L // Также рекомендую вам ознакомиться с паттерном Fluent Builder — // http://goo.gl/HIulBb // И да, паттерны обычно тем или иным способом решают какую-то // прикладную задачу. Поэтому "приступать к изучению паттернов," не // имея под рукой хорошей задачи — это довольно плохая идея. Придумайте // себе хорошую задачу и пытайтесь применять паттерны для ее решения, а // не придумывайте задачи, чтобы применить паттерны. // Хороший пример такой задачи как раз и приводится в книге GoF // (http://www.amazon.com/dp/0201633612), где они step-by-step // разрабатывают текстовый редактор. Car tempCar = null; if (carBuilder != null) { carBuilder.buildCategory(pCategoryCar); carBuilder.buildCarcass(pCarcassType); carBuilder.buildDoors(pDoors); carBuilder.buildEngine(pEngine); carBuilder.buildColorCarcass(pColorCar); // Я, кстати, вообще не понимаю, как это может работать, поскольку // в вашем коде метод getBuiltCar всегда возвращает null. tempCar = carBuilder.getBuiltCar(); } else { // Простите, здесь у меня просто глаза вытекли. final String nullPoint = "Yoa are not create car_builder"; Exception exception = new NullPointerException(nullPoint); exception.printStackTrace(); } return tempCar; } } // Какое сказочное название для класса :) public class RunBuilderPattern { public static void main(String[] args) { Car car = null; Director director = new Director(); MersedesBenzBuilder mersedesBenzBuilder = new MersedesBenzBuilder(); // Ух ты! Смотрите! Я на самом деле работаю как простой конструктор, // но два раза разворачиваю и заворачиваю параметры. car = director.buildNewCar( mersedesBenzBuilder, CategoryCar.SPORT_CAR, CarcassType.CABRIOLET, Car.TWO_DOORS, Engine.PETROL, ColorCar.RED); System.out.println("CAR INFO: " + car.getCarInfo()); } } // ... а вот эти штуки неплохо бы переместить в класс Car. public enum CarcassType { SEDAN, UNIVERSAL, CABRIOLET, HATCHBACK, COUPE, LIMOUSINE } public enum CategoryCar { CARGO, PASSENGER, BUS, SPORT_CAR } public enum ColorCar { BLACK, WHITE, RED, GREEN, BLUE, ORANGE, BROWN } public enum Engine { DIZEL, PETROL, GAZ, HUBRID }

Комментариев нет:

Отправить комментарий