#java #ооп #архитектура
Я решаю задачу о нахождении лидера (leader election)
Это чисто алгоритмическая задача, у которой есть 2 формы: однонаправленное кольцо
и двунаправленное. Для представления данных я создал свой собственный класс для списка,
закрученного в кольцо. То есть у меня два алгоритма, по одному для каждой формы задачи.
public abstract class MyAbstractRoundList {
protected int size;
protected Agent[] arr = new Agent[1];
protected int index = 0;
public boolean isEmpty() {
return size == 0;
}
public int size() {
return size;
}
public void add(Agent agent) {
if (size >= arr.length) {
Agent[] temp = arr;
arr = new Agent[temp.length * 2];
System.arraycopy(temp, 0, arr, 0, temp.length);
}
arr[size++] = agent;
}
// и дальше еще много методов
Далее я создаю наследников этого класса для однонаправленного режима и для двунаправленного.
Реализации в них конечно различаются. Далее, у меня есть класс:
public class LeaderElection {
public static void solve(MyAbstractList list, int i) {
if (i == 0) {
list.initiateStartState();
} else {
list.setMessages();
}
}
}
В нем у меня решение для однонаправленного режима. И есть похожий класс с решением
для двунаправленного, но реализации конечно тоже различаются. Что бы не прописывать
в клиентском коде
if(isOneDirectMode()){
LeaderElection.solve();
{
if(isBiDirectMode()){
BiLeaderElection.solve();
}
Я мог бы так же создать абстрактный класс для решения и его наследников и пользоваться
полиморфизмом (так я и делаю для списков). Но вот тут и возникает проблема: у меня
получаются параллельные иерархии наследования. Если появится новый режим я должен буду
добавить новый класс подкласс MyAbstractRoundList и новый подкласс решения. Можно как
то избавиться от этого? Я вижу только один способ: Можно было бы сделать в MyAbstractRoundList
абстрактный метод solve() и реализовывать его в подклассах каждого режима так как это
требуется для конкретного режима. Но это плохо, потому что тогда у меня подклассы MyAbstractRoundList
будут не только хранить данные, но и решать задачу. То есть выполнять 2 функции.
Ответы
Ответ 1
Они не параллельны. Изменения в одной иерархии никак не влекут за собой изменения в другой иерархии. Если появится новый режим я должен буду добавить новый класс подкласс MyAbstractRoundList и новый подкласс решения. Можно как то избавиться от этого? Я вижу только один способ: Можно было бы сделать в MyAbstractRoundList абстрактный метод solve() и реализовывать его в подклассах каждого режима так как это требуется для конкретного режима. Решение задачи - это поведение. Поведение лучше описывать интерфейсами. public interface Solver { void solve(MyAbstractList list, int i); } ... public class LeaderElection implements Solver { ... public class BiLeaderElection implements Solver { ... public class MultiTreadingLeaderElection implements Solver { У вас отдельная иерархия наследования классов, отвечающих за хранение данных, и отдельная иерархия классов, отвечающих за решения. Это нормально. Если появится новый вид хранения (новый тип списка), вы добавите новый класс-наследник MyAbstractRoundList. Это никак не связано с решениями, новый класс может использоваться и существующими решениями. Если потребуется добавить новый способ решения, вы добавите новый класс, реализующий интерфейс Solver, и это не обязательно должно отразиться на иерархии классов для хранения. При таком подходе у вас всегда будет работать код solver.solve(abstractList, i);, если в переменную solver положить объект любого класса, реализующего интерфейс Solver, а в переменную abstractList - любой объект класса-наследника MyAbstractRoundList, и самое главное - этот код не придется менять при добавлении новых классов, как и должно быть с точки зрения ООП. UPD. Есть данные, есть поведение. В рамках класса поля описывают данные, методы - поведение. Например, я руковожу бригадой роботов. Одни из них могут строить, другие - переносить, третьи - разрушать, четвертые - поливать. Робот-строитель, например, может принести себе стройматериал, но только в небольшом количестве, таким образом, он может и строить, и носить. А робот-носитель - только носить. И если вдруг поступает задача срочно разгрузить вагон стройматериалов, то мне надо собрать всех роботов, умеющих носить. И мне плевать, что это за роботы. Хоть робот-поливалка. Если может носить - пусть идет носить. Я просто выберу роботов с нужным мне поведением, и буду уверен, что они смогут сделать то, что мне надо. public interface Carrier { void carry(); //нести } public interface Builder { void build(); //построить } public interface Destroer { void destroy(); //сломать } public class RobotBuilder extends Robot implements Builder, Carrier { public void carry() { } public void build() { } } public class RobotDestroer extends Robot implements Destroer { public void destroy() { } } public class RobotCarrier extends Robot implements Carrier { public void carry() { } }
Комментариев нет:
Отправить комментарий