How to understand how to allocate "classes" when designing a program? I understand that classes are nouns on which actions (methods) are performed, but when it comes to the point, I get lost. Immediately a bunch of "nouns" climbs into my head - as a result, a stupor! How to learn to think in classes?
4 answers
A little wrong question is posed: you need to think not with classes, but with objects. There are several levels. The first level is conceptual. One of the programming paradigms says that the object is solely responsible for its behavior and this responsibility must be clearly defined. First of all, when considering a task, an object should be defined as a set of certain obligations. The second level is the specification level. This is when you have already defined objects as a set of certain obligations, you consider these objects as a set of methods. And the third level is the level of implementation. This is when you already have an object as a set of obligations and you already know what you should do (a set of methods), you actually think over the contents of the class. That is, you define fields, methods, constructors.
Example, station. Passengers arrive at the station, find the way from which the train leaves. The train comes at a certain time to a certain path. Passengers board the train. The train takes them to their destination. Here you can select several objects
1) Passenger - he is obliged to come to the station, find out the way, come on the way at a certain time, take the train;
2) Train - drive a certain way at a certain time, know and take passengers to their destination;
3) Station - should change the tabloid, which has the necessary information for passengers.
This we have reviewed the conceptual level. We have already identified the objects and understand how they interact with each other. Actually it was a question, so we will not continue to design.
I would not say that a class is a “noun”. Did you mean "essence"? This is closer to the truth.
I would advise to think of classes as a kind of primitive, thanks to which you can describe the real (in the understanding of the human world) thing (object).
For example, the most simple example that can be shown both classes and inheritance at once is a point that has only the coordinates [x, y] (for simplicity, we take on the plane). Further from the point, you can draw shapes, for example, a circle, centered at a certain point, you simply add a radius to the coordinates, and in fact this is enough to describe two primitives.
But this is not enough to perform actions on primitives. This is where you start designing your class methods. Let's start with a point. Basic methods (except, of course, the constructor, which in the simplest case specifies the coordinates of a point) will be, for example, its movement / movement along coordinate axes (these may be variations on the topic: redrawing in a new location or calculating new coordinates by some algorithm, for example according to the formula y = kx + b).
For the circle, you will already use the previously written methods to draw the center point and the points of the circle itself with radius R.
That is, designing or “allocating” classes is reduced to representing (this is your task) the description of a real object in the form of the most appropriate (optimal) data structure + designing or “highlighting” methods for working with this data.
And why optimality is important, you can store all points of a circle and easily recalculate their new coordinates, or you can store only the center point and radius, but with more complex (relatively) calculations, calculate their new location. A kind of compromise between the complexity of the calculations (on which the processing speed depends) and the memory occupied.
- Plus. In general, the problem is that as a class, you can implement many entities. And among them may be redundant, for example, responsible only for the interaction of any objects. I see only two things that can help - a sensible approach and gaining practice. And the vehicle can advise you to give a real example of the task, which causes complexity, and each of us could bring his own vision of the optimal breakdown into classes. - gecube
You can also add to the answer:
Each class should be responsible only for itself and the operations that it must perform.
Also one of the principles of OOP is hiding data in a class that other classes do not need. Those. If we take a simple example proposed by Dex, then we will represent the class Линия . It will have two points of type целое - x and y . Well, these points will be hidden for other classes. And all the calculations will be performed with these points strictly inside the class Линия . Well, make sure that from other classes you can set x and y . To do this, the class will have methods to установитьX and установитьY . Example:
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() { //Класс фигуры чертит сам себя } } Now, working with shapes is easier:
public static void main() { //ИНициализируем объект класса с устанавливаем данные, нужные ему для работы Line line = new Line(); line.setX(5); line.setY(20); //Теперь делаем то, что от класса нам надо - чертим линию. line.draw(); } PS Here it would be better to do inheritance, but such an example is the simplest.
- Well, as if yes, but we have already dealt with this issue with this user. The only thing that didn’t sound was “to take responsibility for myself,” but it would be necessary. - Dex
Usually, we are taught in universities:
Each class should be responsible only for itself and the operations that it must perform.
This is correct, but not always true. A class is really an entity mapping, just don't take this statement literally. Suppose we have a loader and a set of different goods, is each individual product responsible for its own loading? No, the loader himself decides how to ship a particular product considering the recommendations on the package (if there are any, if there are none, he decides on the basis of his own experience).
The essence of breaking up into classes is the grouping of a logically related functional. Sometimes for this it is sufficiently grouped in entities, sometimes not. Example:
Entity grouping:
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(); Functional grouping:
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()); Each option has its advantages and disadvantages.
In real-world applications, often all classes can be divided into 2 groups:
- Data Containers (POJO Classes aka JavaBeans)
- Controllers (perform operations on data)