B
B
Ben L2020-10-11 11:29:53
OOP
Ben L, 2020-10-11 11:29:53

When to use private fields when defining a class?

There is an opinion that when a class is written, you need to make most of the properties private so that no one can change the internal state of objects, thus reducing unpredictable situations. Others explain that the state of the object is the affairs of the object (it decides how to change it). And in order to influence the object, we can call its public methods, and it will change (if necessary) the state.

But if you do everything private, then we will not have access not only to change but also to read, and this is often necessary. In this regard, I had a question how to deal with such a situation: make getters (for all or only some properties), make methods that would use private properties, or make them public and leave the responsibility of correct changes to the programmer?

Example: there is a rectangle (Rectangle) which we want to draw on some canvas (Canvas)

class Rectangle {
  private x, y, w, h: number;

  constructor(x, y, w, h: number) {
    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;
  }

  // ......
}

class Canvas {
  private w, h: number;

  constructor(w, h: number) {
    this.w = w;
    this.h = h;
  }	

  public drawRect(rect: Rectangle) {
    // Здесь нужны координать и размеры прямоуголника
  }
}

const rect = new Rect(0, 0, 10, 15);
const canvas = new Canvas(20, 40);
canvas.drawRect(rect);


The drawRect method needs the coordinates and dimensions of the rectangle, should they be made public or do getters for all these properties?

Or maybe you should pass x, y, w, h to the drawRect method and also do the draw method on the rectangle

class Rectangle {
  private x, y, w, h: number;

  constructor(x, y, w, h: number) {
    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;
  }

  public drawOnCanvas(canvas: Canvas) {
    canvas.drawRect(x, y, w, h);
  }
}

class Canvas {
  private w, h: number;

  constructor(w, h: number) {
    this.w = w;
    this.h = h;
  }	

  public drawRect(x, y, w, h: number) {
    // Есть все что нужно для рисования !!!!
  }
}

const rect = new Rect(0, 0, 10, 15);
const canvas = new Canvas(20, 40);
rect.drawOnCanvas(canvas);


Well, or do everything publicly and use what you need and when you need it?

Answer the question

In order to leave comments, you need to log in

3 answer(s)
I
Ilya, 2020-10-11
@sarapinit

if you have typescript you can do this

class Rectangle {
  readonly x: number
  readonly y: number
  readonly w: number
  readonly h: number
    
  constructor(x: number, y: number, w: number, h: number) {
    this.x = x
    this.y = y
    this.w = w
    this.h = h
  }
}

class Canvas {
  private w: number
  private h: number

  constructor(w: number, h: number) {
    this.w = w;
    this.h = h;
  }	

  public drawRect(rect: Rectangle) {
    //draw implementation
  }
}

In this case, the Rectangle is just a container for numbers and no one can change it. But the fields are public and that allows you to draw it.
But this solution is valid only for this case. In general, the question is deeper and does not have a single answer.
I recommend reading about the Rich model and Anaemic model , this will give some understanding about the difference in approaches and what are the pros / cons.

H
HemulGM, 2020-10-11
@HemulGM

You are using almost nothing meaningful from OOP here. I'm writing an example in pascal, but it doesn't change anything.
The standard solution would be to create a "Drawable" base class, one that can be drawn onto the canvas.
Let's say TSprite. And we will declare a virtual Draw method so that we can ask to be drawn.

TSprite = class //ну или интерфейс
public
  procedure Draw; virtual; abstract;
end;

Now let's write a TRectangle class based on the TSprite class. Where we will also declare the Draw method, but we will write the drawing there. Well, let's declare private size and position fields with the constructor.
TRectangle = class(TSprite)
private
  FX, FY, FH, FW: Float;
  FCanvas: TCanvas;
public
  procedure Draw; override;
  constructor Create(Canvas: TCanvas; const X, Y, H, W: Float); 
end;

constructor TRectangle.Create(Canvas: TCanvas; const X, Y, H, W: Float); 
begin
  FCanvas := Canvas;
  FX := X;
  FY := Y;
  FH := H;
  FW := W;
end;

procedure TRectangle.Draw;
begin
  FCanvas.Rectangle(FX, FY, FH, FW); // Это отрисовка фигуры на канве
end;

Now we can create a TRectangle class, specify the target canvas and dimensions.
Rect := TRectangle.Create(Canvas, 10, 10, 40, 45);
Rect.Draw; // Всё, можно отрисовать

After all this, we can create other shapes or objects that will also inherit from TSprite. We can put them in a list of objects of type TSprite and access each object through Draw. For example, in the rendering cycle of all objects.

V
Vladimir Korotenko, 2020-10-11
@firedragon

In primitive classes just fields also should be public. What would be less noodles. But this does not apply to the presentation. That is, you have a rectangle and a rectangle stored in the database. In the first case, through the fields, in the second, only through a method that saves to the database and then changes the fields

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question