Страницы

Поиск по вопросам

среда, 11 декабря 2019 г.

Классы в ООП

#ооп #классы #java


Как понять, каким образом выделять "классы" при проектировании программы? Я понимаю,
что классы - это существительные, над которыми совершаются действия(методы), но когда
доходит до дела, я теряюсь. Сразу куча "существительных" в голову лезет - в итоге ступор!
Как научиться мыслить классами?    


Ответы

Ответ 1



Немножко неправильно вопрос поставлен: мыслить нужно не классами, а объектами. Есть несколько уровней. Первый уровень - концептуальный. Одна из парадигм программирования гласит, что объект несет полную ответственность за свое поведение и эта ответственность должна быть четка определенна. В первую очередь при рассмотрении задачи, следует определить объект как совокупность неких обязательств. Второй уровень - уровень спецификаций. Это когда у вас уже определенны объекты как совокупность неких обязательств, вы рассматриваете эти объекты как совокупность методов. А третий уровень - уровень реализации. Это когда у вас уже есть объект как совокупность обязательств и вы уже знаете, что должен делать (совокупность методов), вы, собственно, продумываете, содержимое класса. То есть определяете поля, методы, конструкторы. Пример, вокзал. Пассажиры приезжают на вокзал, находят путь с которого отправляется поезд. Поезд приходит в определенное время к определенному пути. Пассажиры садятся в поезд. Поезд везет их в место назначения. Тут можно выделить несколько объектов 1)Пассажир - он обязан приехать на вокзал, узнать путь, прийти на путь в определенное время, сесть в поезд; 2)Поезд - подъехать на определенный путь в определенное время, знать и отвезти пассажиров в место назначения; 3)Вокзал - должен менять таблоид, на котором есть нужная информация для пассажиров. Это мы рассмотрели концептуальный уровень. Мы уже выделили объекты и понимаем как они взаимодействуют между собой. Собственно это был вопрос, поэтому продолжать проектировать не будем.

Ответ 2



Не сказал бы, что класс - это "существительное". Возможно, вы имели ввиду "сущность"? Это ближе к истине. Я бы посоветовал думать о классах как о некотором примитиве, благодаря которому вы можете описать реальную (в понимании мира человеческого) вещь (объект). Например, наиболее простой пример, которым можно показать и классы, и наследование разом - это точка, имеющая только координаты [x, y] (для простоты, возьмем на плоскости). Далее из точки, можно рисовать фигуры, например, окружность, с центром в некоторой точке, собственно к координатам вы добавите просто радиус и по сути этого достаточно, чтобы описать два примитива. Но, этого не достаточно чтобы выполнять действия над примитивами. Здесь вы приступаете к проектированию методов вашего класса. Начнем с точки. Базовые методы (кроме, естественно, конструктора, который задает в простейшем случае координаты точки) будет, например ее движение/перемещение по координатным осям (это могут быть вариации на тему: перерисовка в новом месте или расчет новых координат по какому-то алгоритму, например по формуле y=kx+b). Для окружности, вы уже будете использовать написанные ранее методы для прорисовки центральной точки и точек самой окружности с радиусом R. Т.е проектирование или "выделение" классов сводится к представлению (это ваша задача) описания реального предмета в виде наиболее подходящей (оптимальной) структуры данных + проектирование или "выделение" методов для работы с этими данными. Причем, почему важна оптимальность, вы можете хранить все точки окружности и легко пересчитывать их новые координаты, а можете хранить только центральную точку и радиус, но более сложными (относительно) вычислениями рассчитывать их новое местоположение. Своеобразный компромисс между сложностью вычислений (от которой зависит скорость обработки) и занимаемой памятью.

Ответ 3



Можно еще добавить к ответу: Каждый класс должен нести ответственность только за самого себя и операции, которые он должен выполнять. Также один из принципов ООП - сокрытие данных в классе, которые не нужны другим классам. Т.е. если взять простой пример, предложенный Dex, то представим класс Линия. В нем будут две точки типа целое - x и y. Ну так вот эти точки будут скрыты для других классов. А вычисления все будут выполняться с этими точками строго внутри класса Линия. Ну и позаботиться о том, чтобы из других классов можно было установить x и y. Для этого в классе будут методы установитьX и установитьY. Пример: public class Line { private int x; private int y; public void setX(int x) { this.x = x } public void setY(int y) { this.y = y; } //private методы, производящие все необходимые операции с x и y //... .... ... // public void draw() { //Класс фигуры чертит сам себя } } Теперь, работать с фигурами проще: public static void main() { //ИНициализируем объект класса с устанавливаем данные, нужные ему для работы Line line = new Line(); line.setX(5); line.setY(20); //Теперь делаем то, что от класса нам надо - чертим линию. line.draw(); } P.S. Тут лучше бы делать наследование, но такой пример самый простой.

Ответ 4



Обычно этому нас учат в университетах: Каждый класс должен нести ответственность только за самого себя и операции, которые он должен выполнять. Это правильно, но не всегда верно. Класс это действительно отображение сущности, только не надо воспринимать это утверждение буквально. Предположим у нас есть грузчик и набор разных товаров, разве каждый отдельный товар сам отвечает за свою погрузку? Нет, грузчик сам решает как грузить тот или иной товар учитывая рекомендации на упаковке (если такие вообще имеются, если их нет он решает исходя из собственного опыта). Суть разбития на классы это групировка логично связанного функционала. Иногда для этого достаточно групироки по сущностям, иногда нет. Пример: Групировка по сущностям: interface IDrawable { void paint(); } class Point implements IDrawable {...} class Line implements IDrawable {...} class Square implements IDrawable {...} // использование new Point().paint(); new Line().paint(); new Square().paint(); Групировка по функционалу: class Point {...} class Line {...} class Square {...} class DrawingManager { public void draw(Point p) {...} public void draw(Line l) {...} public void draw(Square s) {...} } // использование new DrawingManager().draw(new Point()); new DrawingManager().draw(new Line()); new DrawingManager().draw(new Square()); У каждого варианта есть свои преимущества и недостатки. В реальных приложениях часто все классы можно условно разделить на 2 группы: Контейнеры данных (классы POJO ака JavaBeans) Контроллеры (выполняют операции над данными)

Комментариев нет:

Отправить комментарий