Is it a good practice for a class to have only public (no private or protected) methods and variables?

Viewed 824

recently I am doing a project for a school course. I always declare all variables and methods inside every classes public because It helps me access those variables easier while developing and less coding for the get(); and set(); functions. However, i think this is the wrong way of doing OOP. Any ideas?

2 Answers

Getters/Setters are useful sometimes, but aggregates are also useful.

If you have an aggregate, you should be willing to accept any data that matches the types of your data fields. If you want to maintain invariants (width>height) and assume it elsewhere in your code, you'll want accessors.

But code that doesn't assume invariants is often easier to work with and can even be less bug prone; manually maintaining invariants can get extremely hard, as messing up or compromising even once makes the invariant false.

Honestly, the biggest advantage of getters/setters is mocking (making test harnesses) and putting a breakpoint at access/modification. The costs in terms of code bulk and the like are real, and having more of the code you write not be boilerplate has value.

So a width/height field on a non-"live" rendered rect? Default to public data. A buffer used to store the data in a hand written optional<T>? Private data, and accessors.

Accessors should be used to reduce your own (or the code reader's) cognitive load. Write code with a purpose, and don't write code that doesn't have a purpose.

Now you'll still want to know how to write getters/setters, so practicing on stupid "rect width/height" cases has value. And learning the LSP problem that while a ReadOnly square is a kind of ReadOnly rect, a ReadWrite square is not a kind of ReadWrite rectangle might be best done via experience (or maybe not, as so many people experience it but don't learn the lesson).

This pertains to the principle of encapsulation where exposing internals means, from the perspective of the class in question ("you"):

  • You have no control over what is written to these fields
  • You are not notified if these fields are accessed
  • You are not notified if these fields are changed
  • You can never trust that the values are valid
  • You cannot change the types of these values without impacting any code that uses them

When you encapsulate you control access to these properties meaning:

  • You can prevent alterations
  • You can validate before writing, and reject invalid values
  • You can change the internal representation without consequence, provided the get/set functions still behave the same way
  • You can clean up the values before they are written
  • You have confidence that at all times the values are valid since you are the gatekeeper
  • You can layer additional behaviour on before or after changes have been made, such as the observer pattern

This is not to say you must use encapsulation all the time. There are many cases when you want "dumb data" that doesn't do anything fancy, it's just a container for passing things around.

In C++ this often leads to the use of struct as "dumb data" since all fields are public by default, and class as "smart data" as the fields are private by default, even though apart from the access defaults these two things are largely interchangeable.

Related