Как добиться полиморфизма с помощью принципа разделения интерфейса?

Моя цель — понять принцип разделения интерфейса и одновременно добиться полиморфизма.

Мой ожидаемый результат: я могу добиться полиморфизма с помощью принципа разделения интерфейса.

Мой фактический результат: Нет, я не могу. Я вынужден создать шаблон и использовать принцип замещения Лискова (если есть рабочий, должен быть рабочий, который не может есть, поэтому создайте интерфейс для рабочего, который может есть, который расширяет рабочего). Я думаю, что неправильно понимаю принцип разделения интерфейса.

Это код, который нарушает принцип разделения интерфейса.

public interface IWorker {
  void work();
  void eat();
}

class Human implements IWorker {
  public void work() {
    System.out.println("Human is working.");
  }

  public void eat() {
    System.out.println("Human is eating.");
  }
}

class Robot implements IWorker {
  public void work() {
    System.out.println("Robot is working.");
  }

  public void eat() {
    throw new UnsupportedOperationException("Robot cannot eat");
  }
}

Мне сказали разделить интерфейс на 2.

public interface IEatable {
  void eat();
}

interface IWorkable {
  void work();
}

class Human implements IWorkable, IEatable {
  public void work() {
    System.out.println("Human is working.");
  }

  public void eat() {
    System.out.println("Human is eating.");
  }
}

class Robot implements IWorkable {
  public void work() {
    System.out.println("Robot is working.");
  }
}

Решение состоит в том, чтобы использовать принцип подстановки Лисков.

public interface IWorkable {
  void work();
}

interface IEatable {
  void eat();
}

interface IWorker extends IWorkable {
}

interface IHumanWorker extends IWorker, IEatable {
}

person kidfrom    schedule 24.05.2021    source источник
comment
Я не совсем понимаю, в чем твоя проблема. Использование IWorkable в качестве, скажем, аргумента функции уже позволяет вам использовать полиморфизм.   -  person Raildex    schedule 24.05.2021


Ответы (2)


Ваш второй шаг выглядит хорошо, вы разделили интерфейс на два более конкретных интерфейса. Роботу нет смысла есть. (Я действительно не понимаю третий шаг)

На стороне вызывающего абонента теперь вы можете работать со своими абстракциями:

//Polymorphism
List<IWorkable> workers = Arrays.asList(new Robot(), new Human());
//do some work

List<IEatable> eaters = Arrays.asList(new Human(), new Human());
//send for lunch break

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

Робот не является IWorker (ваш первый код), потому что он не полностью заполняет (полный) контракт (интерфейс, метод еды), каким бы похожим он ни казался.

person HectorLector    schedule 25.05.2021
comment
Ах. Я вижу, вы используете интерфейс на самом низком уровне (не нужно создавать IWorker или IHumanWorker). Это то, что я ищу, тогда первый код не проблема. - person kidfrom; 25.05.2021

Я бы рекомендовал использовать абстрактные классы вместо интерфейсов. Если вам нужно, чтобы каждый Workable работал, вы делаете метод абстрактным. Если это необязательно, что они едят, вы не едите. Примером этого может быть:

abstract class Workable {

    protected String name;

    public Workable(String name) {
        this.name = name;
    }

    protected abstract void work();

    public void eat() {
        System.err.println("\"" + name + "\" can't eat");
    }
}

class Human extends Workable {
    public Human(String name) {
        super(name);
    }
    @Override
    public void work() {
        System.out.println("Human " + name + " is working!");
    }

    @Override
    public void eat() {
        System.out.println("Human " + name + " is eating!");
    }
}

class Robot extends Workable {
    public Robot(String name) {
        super(name);
    }

    public void work() {
        System.out.println("Robot " + name + " is working!");
    }
}


public class Test {
    public static void main(String[] args) {
        Workable[] workers = new Workable[] {
                new Human("Jerry"),
                new Robot("XAE12")
        };
        for (Workable worker : workers) {
            worker.work();
            worker.eat();
        }
    }
}

Я не уверен, правильно ли я понял ваш вопрос, поэтому, пожалуйста, дайте мне знать, помогло ли это вам.

person Cactus Coder    schedule 24.05.2021
comment
Я думаю, что это нарушает принцип замещения Лисков в обратном порядке? У робота не должно быть метода еды. Или LSP действительно рекомендует это? Мне нравится идея предположить ошибку метода в абстрактном классе, поэтому вы вынуждены переопределять метод на каждом новом клиенте. - person kidfrom; 24.05.2021
comment
Вот почему у робота нет метода еды. Я только что понял, что забыл опубликовать вывод, я отредактирую его, когда снова буду за своим компьютером. Вы также можете создать исключение в исходном методе, если хотите, главное, чтобы вы не переопределяли метод eat в классе робота. - person Cactus Coder; 24.05.2021