Я решаю задачу о нахождении лидера (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 функции.
Ответ
Они не параллельны. Изменения в одной иерархии никак не влекут за собой изменения в другой иерархии.
Если появится новый режим я должен буду добавить новый класс подкласс
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() {
}
}
Комментариев нет:
Отправить комментарий