Страницы

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

четверг, 22 ноября 2018 г.

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

Здравствуйте !!! Недавно, я приступил к изучению паттернов проектирования. Один из самых первых паттернов, который был реализован - это 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 Жду Ваших замечаний, советов, ругани и брани !!!


Ответ

Всегда пожалуйста: 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 }

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

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