Java. Абстракции, интерфейсы, наследования. Как реализовать?

Захотелось получше поизучать ООП, а именно проектирование, уровни абстракции и т.д. В кач-ве языка взял Java.
Сейчас пишу проект, дабы познать дзен и абстракции над абстракциями и возникло несколько вопросов.
В кач-ве примера буду говорить о людях (Human).

К примеру, есть основной интерфейс IHuman, в котором описаны методы, void say(String text), void jump().
Также есть класс Human с конструктом Human(String name, Boolean kaduk). Но объекты в программу попадают "извне" (в моем случае БД), значит нужен метод который будет распаршивать данные из таблицы и возвращать коллекцию List. В моем случае это статический публичный метод, который находится в классе Human. Правильно ли это? Или же такой метод должен находится статически в самом интерфейсе? Или ...?

Второй момент. Люди различаются по полу и на фоне этого признака ведут себя по разному при вызове методов. В случае с say - их поведение одинакого, но вот jump() абсолютно разный. Также, когда коллекция наполняется данными (людьми), мы не знаем какой именно объект нам подсовывают, это определяется потом, вызовом метода void checkKaduk(). Мое видение на весь этот сексизм следующий:
Создаем абстрактный класс AHuman, который реализует IHuman, описываем в нем всю структура класса (Свойства, конструктор) и реализуем метод say. Но вот дальше не совсем понимаю как сделать определение пола, вызов нужного метода jump.
Необходимо сделать две реализации по типу public class HumanManImpl extends AHuman { }, и такой же для Woman и в обоих перееопределить jump, но в таком случае наш парсер из БД работать не будет, т.к. тут сразу нужно знать какой тип нужен, а инициализивать абстрактный класс нельзя.
Если я правильно понимаю, то нужно делать еще один класс, который будет поддерживать генерики, в момент парсинга из БД мы не знаем какая именно реализация нужна и указываем <?>, далее для каждого объекта из нового класса с генериками вызываем метод, который будет узнавать пол по наличию кадыка, и преобразовываться(?) в нужный тип. Но не могу понять как это все реализовать. Даже если мои домыслы не верны, то все-ровно хотелось бы узнать ответ, как реализовать вышеописанное.

Небольшие наброски кода:
// Основной интерфейс
public interface Human {
    void say(String text);
    void jump();
}

// Абстрактный класс
public abstract class AHuman implements Human {
    public String name;
    public Boolean kaduk;

    public AHuman(String name, Boolean kaduk) {
        this.name = name;
        this.kaduk = kaduk;
    }

    public void checkKaduk() {
        if (kaduk) {
        } else {
        }
    }

    @Override
    public void say(String text) {
        System.out.println(String.format("%s: %s", name, text));
    }
}

// Реализация для Man
public class HumanManImpl extends AHuman {
    public HumanManImpl(String name, Boolean kaduk) {
        super(name, kaduk);
    }

    @Override
    public void jump() {
        System.out.println("Man");
    }
}

// для Woman
public class HumanWomanImpl extends AHuman {
    public HumanWomanImpl(String name, Boolean kaduk) {
        super(name, kaduk);
    }

    @Override
    public void jump() {
        System.out.println("Woman");
    }
}
  • Вопрос задан
  • 1009 просмотров
Решения вопроса 1
@nagibator8000
Я бы использовал композицию, а не наследование. Создал бы интерфейс Action и от него класс JumpAction. Потом интерфейс Human с одним методом doAction, который бы принимал Action. А уже дальше на основе Human создал классы Man и Woman и реализовывал уникальную логику для каждого класса.

UPD. набросал код
public static interface Action {
        void apply(int power);
        String getActionName();
    }

    public static class JumpAction implements Action {

        private int gravity;

        public JumpAction(int gravity) {
            this.gravity = gravity;
        }

        @Override
        public void apply(int power) {
            if (power > gravity) {
                System.out.println("прыгает на " + (power - gravity) + " метров...");
            } else {
                System.out.println("немогу подпрыгнуть...");
            }
        }

        @Override
        public String getActionName() {
            return "прыжок";
        }
    }

    public static interface Human {
        void doAction(Action action);
    }

    public static class Man implements Human {

        public String name;
        private int power = 10;

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

        @Override
        public void doAction(Action action) {
            System.out.println(name + " делает " + action.getActionName());
            action.apply(power);
        }
    }

    public static class Woman implements Human {
        public String name;
        private int power = 7;

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

        @Override
        public void doAction(Action action) {
            System.out.println(name + " вертя задницей делает " + action.getActionName());
            action.apply(power);
        }
    }

    public static void main(String[] args) {
        JumpAction jump = new JumpAction(8);
        Man man = new Man("Анатолий");
        man.doAction(jump);
        Woman woman = new Woman("Наталья");
        woman.doAction(jump);
    }

Выдаст
Анатолий делает прыжок
прыгает на 2 метров...
Наталья вертя задницей делает прыжок
немогу подпрыгнуть...
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
@bromzh
Drugs-driven development
Определять какой класс использовать надо извне. Для этого есть всякие Factory-паттерны. А проверку внутри абстрактного класса надо убрать.
А статические методы желательно избегать, хотя в простых factory их можно использовать.

Список надо параметризировать типом Human, а класс параметризировать как . Или просто использовать везде тип Human, как бы полиформизм для того и придуман.

UPD
public interface Human {
    public void jump();
    public void say(String text);
}


public abstract class AbstractHuman implements Human {
    protected String name;

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

    @Override
    public void say(String text) {
        System.out.println(name + ": " + text);
    }
}


public class Man extends AbstractHuman {
    public Man(String name) {
        super(name);
    }

    @Override
    public void jump() {
        System.out.println("Man jump");
    }
}


public class Woman extends AbstractHuman {
    public Woman(String name) {
        super(name);
    }

    @Override
    public void jump() {
        System.out.println("Woman jump");
    }
}


public interface HumanFactory {
    public static Human createHuman(String name, boolean kadyk) {
        if (kadyk) {
            return new Man(name);
        }
        return new Woman(name);
    }
}

import java.util.LinkedList;
import java.util.List;

public class Main  {
    static class DBData {
        String name;
        boolean kadyk;
        public DBData(String name, boolean kadyk) {
            this.name = name;
            this.kadyk = kadyk;
        }
    }

    public static void main(String[] args) {
        List<DBData> source = new LinkedList<>();
        source.add(new DBData("Ваня", true));
        source.add(new DBData("Маша", false));
        source.add(new DBData("Катя", true));
        source.add(new DBData("Сергей", true));
        
        List<Human> humanList = new LinkedList<>();
        
        for(DBData data : source) {
            humanList.add(HumanFactory.createHuman(data.name, data.kadyk));
        }
        
        for(Human human : humanList) {
            human.say("Hello");
            human.jump();
        }
    }
}
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы