#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() { } }
Комментариев нет:
Отправить комментарий