Модель от Гусеницы до Бабочки на Яве - PullRequest
0 голосов
/ 29 сентября 2018

Мне недавно задали этот вопрос в интервью:

Модели животных, которые со временем меняют свое поведение.

  1. Можете ли вы смоделировать бабочку?

    • Бабочка может летать * Бабочка не издает звука
  2. Можете ли вы оптимизировать свою модель для учета метаморфоз от гусеницы до бабочки?

    • Гусеница не может летать
    • Гусеница может ходить (ползать)

Я создал иерархию синтерфейс верхнего уровня (Insect), который имеет 2 интерфейса, расширяющих его (GroundInsect & FlyingInsect).Тогда у меня было Caterpillar реализация GroundInsect и ButterFly реализация FlyingInsect.Тем не менее, я не смог придумать решение для части метаморфоза.Ниже мой код:

Интерфейс насекомого:

public interface Insect { }

Интерфейс FlyingInsect:

public interface FlyingInsect extends Insect {
    public boolean fly();
}

Интерфейс GroundInsect:

public interface GroundInsect extends Insect {
    // Walk/Crawl
    public boolean walk();
}

Класс Caterpillar:

public class Caterpillar implements GroundInsect {

    @Override
    public boolean walk()
    {
        System.out.println("Caterpillar Walk method");
        return true;
    }
}

Класс ButterFly:

public class Butterfly implements FlyingInsect {

    @Override
    public boolean fly() {
        System.out.println("ButterFly Flying method");
        return false;
    }
}

Ответы [ 3 ]

0 голосов
/ 29 сентября 2018

Действительно интересный вопрос.Мне понравилась ваша идея создания интерфейсов GroundInsect и FlyingInsect, но я бы объявил Insect в качестве абстрактного класса, а также Butterfly, чьи дочерние классы будут этапами развития бабочки (включаягусеница и взрослая бабочка).Во всяком случае, вместо того, чтобы писать все отношения, которые могут привести к путанице, я составил диаграмму.Смотрите ниже:

Diagram



Что касается части метаморфоза, я объявил новый интерфейс под названием Metamorphosable (простите меня за странностьимя, но я не мог придумать ничего лучшего!), который содержит метод для обработки метаморфозы насекомого.Более того, конструкторы Chrysalis и AdultButterfly получают в качестве аргумента экземпляр предыдущей стадии разработки.Смотрите мой код ниже:


Насекомое Класс:

public abstract class Insect {
    //...
    //sample method:
    public abstract void eat(); //all insects eats, but each specie (and each of its stages) has its own eating behaviour
}


Бабочка класс:

public abstract class Butterfly extends Insect {}


Метаморфизуемый интерфейс:

public interface Metamorphosable<T extends Insect> {
    public T metamorphosis();
}


Caterpillar класс:

public class Caterpillar extends Butterfly implements GroundInsect, Metamorphosable<Chrysalis>
{
    @Override
    public void eat() {
        //eating behaviour for the caterpillar
    }

    @Override
    public boolean walk() {
        System.out.println("Caterpillar walk() method!");
        return true;
    }

    @Override
    public Chrysalis metamorphosis() {
        return new Chrysalis(this);
    }
}


Chrysalis класс:

public class Chrysalis extends Butterfly implements Metamorphosable<AdultButterfly>
{
    //Constructor
    public Chrysalis(Caterpillar caterpillar) {
        //init a new chrysalis from a caterpillar
    }

    @Override
    public void eat() {
        //eating behaviour for the chrysalis (it eats itself!)
    }

    @Override
    public AdultButterfly metamorphosis() {
        return new AdultButterfly(this);
    }
}


AdultButterfly класс:

public class AdultButterfly extends Butterfly implements FlyingInsect
{
    //Constructor
    public AdultButterfly(Chrysalis chrysalis) {
        //init a new butterfly from a chrysalis
    }

    @Override
    public boolean fly() {
        System.out.println("AdultButterfly fly() method!");
        return false;
    }

    @Override
    public void eat() {
        //eating behaviour for the adult butterfly
    }
}
0 голосов
/ 29 сентября 2018

Давайте оставим пример простым и придерживаемся вашего первоначального подхода.

Сначала я бы представил общий интерфейс, описывающий все виды насекомых:

interface Insect {
    boolean fly();
    boolean walk();
    boolean sound();
}

Методы fly,walk, sound представляют возможные взаимодействия между насекомым и его окружением (в зависимости от характера этих взаимодействий методы могут быть разными и более сложными: возвращать сложные ответы, принимать обратные вызовы и т. Д.).

Ваша первая бабочка была бы просто конкретной реализацией интерфейса:

class Butterfly implements Insect {
    boolean fly() { return true; }
    boolean walk() { return true; }
    boolean sound() { return false; }
}

Теперь давайте добавим возможность преобразования.Опять же, в общем, есть разные способы сделать это, поэтому давайте придерживаться примера бабочки.

Допустим, мы хотим, чтобы гусеница и связанная с ней бабочка были единым целым (мы не хотим, чтобы гусеница все еще бродила, когда бабочка уже существует).

В этом случаеЯ представлял бы и гусеницу, и бабочку как один класс и скрывал бы его текущее состояние в нем.И «состояние гусеницы», и «состояние бабочки» будут содержать различные реализации действий, которые должны измениться после преобразования.Вмещающий экземпляр делегирует свои методы текущему состоянию.

class Butterfly implements Insect {

    private Insect state = new CaterpillarState();

    boolean fly() { return state.fly(); }
    boolean walk() { return state.walk(); }
    boolean sound() { return state.sound(); }

    void transform() { state = new ButterflyState(); }

    private class ButterflyState implements Insect {
        boolean fly() { return true; }
        boolean walk() { return true; }
        boolean sound() { return false; } 
    }

    private class CaterpillarState implements Insect {
        boolean fly() { return false; }
        boolean walk() { return true; }
        boolean sound() { return true; }             
    }
}

Метод transform представляет триггер для метаморфозы.

Здесь ButterflyState и CaterpillarState реализуют то же самое Insect интерфейс как внешний класс.В общем случае я бы определил другой интерфейс для внутренних состояний.

0 голосов
/ 29 сентября 2018

Один из подходов заключается в добавлении третьего контракта, который объединяет два интерфейса.Это потому, что я считаю, будь то бабочка или гусеница, это должен быть тот же самый экземпляр Insect.

Ниже приведен специализированный контракт, который добавляет UnsupportedOperationException в качестве опции:

interface FlyingGroundInsect extends FlyingInsect, GroundInsect {

    boolean ableToFly();

    boolean ableToWalk();

    /**
     * As per {@link GroundInsect#walk walk}. This may throw
     * UnsupportedOperationException if the animal's stage doesn't allow flying
     * 
     * @throws UnsupportedOperationException If animal is unable to walk
     */
    @Override
    boolean walk();

    /**
     * As per {@link FlyingInsect#fly fly}. This may throw
     * UnsupportedOperationException if the animal's stage doesn't allow walking
     * 
     * @throws UnsupportedOperationException If animal is unable to fly
     */
    @Override
    boolean fly();
}

И этот единственный класс предназначен для насекомых-бабочек, которые, как понимают, представляют насекомое, которое начинается как гусеница, а затем превращается в бабочку (согласно состоянию age - оно может быть более точным или сложным).

class Butterfly implements FlyingGroundInsect {

    private int age;

    @Override
    public boolean fly() {
        if (!this.ableToFly())
            throw new UnsupportedOperationException("Too early to fly");

        return false;
    }

    @Override
    public boolean walk() {
        System.out.println("Walk method");
        return true;
    }

    @Override
    public boolean ableToFly() {
        return this.age >= 28;
    }

    @Override
    public boolean ableToWalk() {
        return true;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...