2017-01-25 5 views
3

Это, наверное, плохой пример, но, пожалуйста, работайте с ним. У меня есть класс супер Cake и два подкласса: ExampleCake и SaleCake. У меня также есть Baker, который может переделать пирог, чтобы испечь его копию, и знает, что с ним делать потом.Как вы делаете различия в дескрипторах класса утилиты в подклассах?

(Все псевдокод)

public class Cake{ 
    radius; 
    height; 
    ingredients; 
    color; 
    name; 
} 

public class ExampleCake extends Cake{ 
    shelfLocation; 
} 

public class SaleCake extends Cake{ 
    owner; 
} 

public class Baker{ 
    Cake bakeCake(Cake); 
    Cake bakeSaleCake(Cake, Owner); 
    Cake bakeExampleCake(Cake, Location); 

    void handleCake(Cake); 
} 

Пекарь должен знать, как обращаться общие торты, ExampleCakes и SaleCakes. Поэтому его функции bakeCake выглядеть примерно так:

Cake bakeCake(cake){ 
    newCake = cake.copy(); 
    mixIngredients(newCake); 
    putCakeInOven(newCake); 
    putIcing(newCake); 
    return cake; 
} 

Cake bakeSaleCake(cake, owner){ 
    newCake = bakeCake(cake); 
    newCake.setOwner(owner); 
    return newCake; 
} 

Cake bakeExampleCake(cake, location){ 
    newCake = bakeCake(cake); 
    newCake.setLocation(location); 
    return newCake; 
} 

void handleCake(cake){ 
    if(cake instanceof ExampleCake) 
     putOnShelf((ExampleCake)cake); 

    else if(cake instanceof SaleCake) 
     giveToCustomer((SaleCake)cake); 

    else 
     putOnTable(cake); 
} 

Моя проблема состоит в том, что Бейкер зашиты только обрабатывать определенные виды тортов. Если появятся новые виды тортов, он не сможет справиться с ними. Есть ли чистый, общий способ справиться с этим, или это псевдокод, который у меня выше, достаточно «достаточно хороший» (как, например, это не повредит вашим глазам или сердцем)?

Спасибо.

+3

Кажется, что клонированный узор вам просто нужно, чтобы классы 'Cake' реализовали метод для копирования. –

+0

Имеет смысл, насколько создается торт. Я забыл добавить часть «handleCake», которую я добавил в конец псевдокода. – GuitarStrum

+0

Возможно, часть handleCake может быть реорганизована с использованием шаблона посетителя. – Calculator

ответ

1

Для копирования тортов вы можете использовать copy-constructors. Таким образом, зависит от конкретного торта, как он копирует себя. Для обработки произвольных тортов вы можете использовать шаблон посетителя. Через посетителя вы можете «спросить» пирог о его типе, а не проверять тип с помощью instanceof.

Код может выглядеть следующим образом (Вы хотите, чтобы другие торты конструкторов в дополнении к копировальным конструкторам):

// Cake класс

public class Cake{ 
    int radius; 
    int height; 

    public Cake(Cake cake){ 
     this.radius = cake.radius; 
     this.height = cake.height; 
    } 

    <R> R accept(CakeVisitor<? extends R> visitor){ 
     return visitor.visit(this); 
    } 

} 

// ExampleCake класс

public class ExampleCake extends Cake{ 
    String shelfLocation; 

    public ExampleCake(Cake cake, String shelfLocation){ 
     super(cake); 
     this.shelfLocation = shelfLocation; 
    } 

    <R> R accept(CakeVisitor<? extends R> visitor){ 
     return visitor.visit(this); 
    } 

} 

// SaleCake класс

public class SaleCake extends Cake{ 
    String owner; 

    public SaleCake(Cake cake, String owner){ 
     super(cake); 
     this.owner = owner; 
    } 

    <R> R accept(CakeVisitor<? extends R> visitor){ 
     return visitor.visit(this); 
    } 

} 

// Visitor интерфейс

public interface CakeVisitor<R> { 

    R visit(Cake cake); 
    R visit(SaleCake cake); 
    R visit(ExampleCake cake); 

} 

// Бейкер класс

public class Baker { 
    private final CakeVisitor<Void> cakeHandler = new CakeVisitor<Void>(){ 

     @Override 
     public Void visit(Cake cake) { 
      putOnTable(cake); 
      return null; 
     } 

     @Override 
     public Void visit(SaleCake cake) { 
      giveToCustomer(cake); 
      return null; 
     } 

     @Override 
     public Void visit(ExampleCake cake) { 
      putOnShelf(cake); 
      return null; 
     } 

    }; 

    Cake bakeCake(Cake cake){ 
     return processedCake(new Cake(cake)); 
    } 

    SaleCake bakeSaleCake(Cake cake, String owner){ 
     return processedCake(new SaleCake(cake, owner)); 
    } 

    ExampleCake bakeExampleCake(Cake cake, String location){ 
     return processedCake(new ExampleCake(cake, location)); 
    } 

    void handleCake(Cake cake){ 
     cake.accept(cakeHandler); 
    } 

    private <C extends Cake> C processedCake(C cake){ 
     mixIngredients(cake); 
     putCakeInOven(cake); 
     putIcing(cake); 
     return cake; 
    } 
} 

Теперь, если вы хотите обрабатывать новый тип пирога с Бейкером, он будет поставлен на стол по умолчанию, если только вы предоставляете метод посещения, который принимает конкретные объекты нового типа торта. Я думаю, вы не можете сделать его более общим, чем это, потому что, когда у вас есть новый тип торта, вы хотите обрабатывать его иначе, чем другие типы тортов, а новый код, который обрабатывает его, должен куда-то идти. Код типа putOnTable() не должен размещаться внутри класса торта, потому что торт не знает, как его положить на стол - пекарь знает -> поэтому шаблон посетителя.

+0

Это довольно аккуратно. Однако он не компилируется при использовании частного метода C processCake (C cake). Я создал mixIngrediant() и говорит «ссылаясь на отсутствующий тип C». Что такое класс «С» или какой именно синтаксис? Спасибо, что написал это. –

+0

@NickZiebert Метод 'mixIngredients' и другие методы там, в которые я не включал, должны принять торт типа' Cake'. Затем он компилируется, так как общий параметр типа C определяется как потомок торта (потомки «Cake» могут использоваться там, где требуется суперкласс «Cake»). 'processCake' возвращает полученный торт, сохраняя его статический тип. Я попытался сохранить несколько строк в методах выпечки, делая это. – Calculator

0

Хороший дизайн - это определение того, какие изменения поведения и что остается неизменным, а затем разделение этих двух групп. Здесь меняется поведение Бейкера для каждого торта. Поэтому мы хотим создать новый класс, который может инкапсулировать его изменяющееся поведение. Вот как мы это делаем. Создайте интерфейс под названием BakerBehavior с помощью одного метода doBehavior(). Затем создайте класс PutOnShelfBehavior, а другой - GiveToCustomerBehavior, реализующий BakerBehavior. Переопределите метод doBehavior().

Теперь в каждом из ваших классов торта вы можете создать объект PutOnShelfBehavior или объект GiveToCustomerBehavior. Ваш торт Superclass будет иметь метод акт(), который будет выглядеть следующим образом:

//Constructor { 
myBehaviorObject=new PutOnShelfBehavior(); 
} 

public void act() { 
    myBehaviorObject.doBehavior(); 
} 

Теперь в вашем классе Бейкер, это то, что ваш метод handleCake будет выглядеть следующим образом:

private void handleCake(Cake cake) { 
    cake.act(); 
} 

Я не» Нет другого способа сделать это. Это имеет то преимущество, что A) Не нужно жестко кодировать типы торта в этом классе handleCake, B) Вам не нужно повторять код (возможно, у вас есть два разных типа торта, которые помещаются на полку, например). C) Каждый пирог ничего не знает о Бейкере, он просто знает, что он будет помещен на полку или что-то еще. Надеюсь, кто-то скажет мне, есть ли лучший способ сделать это, я заинтригован этим вопросом.