1

I have a Curve class that has some CurveData inside as private member as well as getter functions for topics of interest (e.g. peaks, width and so). So the math logic of a curve is inside this class.

Things to be done with this Curve are:

  • Test its values (returned values from functions) with some thresholds. The test logic is not the same for each instance (few kinds of this curve) and each user may use its own tests.

  • Format its values as string for widgets in GUI;

  • Convert its CurveData to other type of data for other GUI objects.

For now I have 3 , or more different classes for those operations each receiving the Curve as a pointer and analyzing it, formatting it, converting or testing it.

But I wonder if this is the right approach: some people say objects should take care of themselves. Should I move those operations into the Curve class?

I don't feel it would be right, because I want to show it its not part of the Curve logic. On the other side, the Curve operations being all around the code feels bad.

All the process is part of a "use case" where I process some data and at the end showing the results to the user. The design is not critical, since I am not writing a library. But still, I want to know ho to do it the "right" OO way. I have that feeling that I am too data centric - moving data from class to class and each doing its own logic on it.

On the other side, the self-caring Curve class would change each time I will change the text format or I get a new request for additional Tests.

1
  • 2
    thanks for editting , english is not my primary language Commented Jul 10, 2022 at 9:45

3 Answers 3

3

In short

Objects should care about themselves. But this does by far not mean that they have to do everything that is somehow related to them. It's a delicate balance act to separate concerns that do not fully belong together.

Several OO design principles suggest that Curve is best kept lean, focused on the core mathematical concept, without the additional operations (i.e. conversions, tests, formatting). However, your design might need some improvements to prevent data centric approach and hidden coupling.

More details:

Why should the Curve and the transformation operations be kept separate?

  • Single Responisibity Principle: there should be one reason for the class to change. But new formats emerge all the time. If you'd add all possible formats and operations in the Curve class, you might have many more reasons to change, reasons that are independent of the Curve concept.
  • Open/Closed principle: if a new format or a new transformation is needed, your current design would apparently leave Curve untouched; they would extend your current design, which is fine. Regrouping everything in Curve would require to modify the class for every new transformation, whith possible change propagation across the whole software. Maintenance nightmares guaranteed!
  • Interface segregation principle: interfaces should be minimalistic. If everything possibly needed is included in Curve you might impose a cluttered interface. This is not desired for modularity. Not all parts of your software need to know all the details of all possible operations and import all the related dependencies!

From a practical point of view, the fact that you managed intuitively to separate the core of the Curve concept from the rest seems to confirm the feasibility of a less coupled design. In this regard, the GoF builder pattern, and the strategy pattern among others, are techniques used to separate the core of an abstraction from the changing representation or algorithms.

But is your design as robust as you'd like?

  • You need to make sure that your externalised operations use Curve's public interface and do not access to its internals. This should prevent the data centric approach and favour an abstraction-oriented approach
  • An essential self-test criteria is: "Could I replace my curve implementation with a completely different one, and would the transformation still work?". If not, you would weaken encapsulation and might end-up with lots of hidden coupling and that would be the object oriented version of spaghetti code.
  • In practice you could check this, making the Curve class abstract, without any data nor implemented functions. It would be Curve a black box. Your current Curve would be refactored to become an implementation of the abstract Curve. Refactor all your operations to work only on an abstract curve.

In case of major issues, you may come back here with a more specific question.

3
  • that what i have done , but then find my self having : CurveFormatter, CurveConverter,CurveTester,CurveXXXX_er classes and its seems weird that al this other class can only do Curve related operations Commented Jul 10, 2022 at 9:47
  • CurveFormatter(Curve* curve) or should Curve have an ICurveFormatter? Commented Jul 10, 2022 at 9:49
  • 2
    @user3717741 What would be useful is to make broader classes for your operations on the curve, i.e. ICurveFormatter, ICurveConverter, ICurveTester, and derive the many other from the kind of operation you are planning: this allows you to code some general common logic for going through the curve, and override particular calculations. You may make these classes take a curve pointer in there constructor or take it as argument for some member functions: there's no generally best approach here.
    – Christophe
    Commented Jul 10, 2022 at 10:11
1

I see this a lot: a class has a ton of methods, all vaguely related to possible use cases of that class but certainly not core logic of it. Then I look at where some of these methods are used. They typically have one reference because someone at some moment needed some additional processing to get their job done and needed a place to put that logic. Those people like short powerful code so they are reluctant to write the code where it is needed (in the part they are creating), that would make their code look bloated. So they hide it in the class they are dealing with. Problem solved.

Needless to say this makes everyone else's life harder. The better way would be to keep that helper logic close to the code that needs it. No one else cares about it or would want to spend brain power on the noise created in the serving class, time and time again.

Whether you write it inline or add some private static method in your own class or create a new CurveMonkey class for all those handy-dandy methods is secondary. Anything is better than polluting the domain class with it.

1

The way I read this question it's asking: should I smoosh everything into the Curve class or have a bunch of CurveThis and CurveThat classes?

Allow for both. I present the façade pattern.

enter image description here

I know you're talking about Curves and this is talking about Videos but the idea here is that what's hiding behind an abstraction doesn't always have to be simply some methods jammed into a single class. It can be a whole object graph. Just hide that stuff so the code that uses it can't see that.

When designing a complex system there is a lot of pressure to keep an interface simple, small, and useable. There is a lot of pressure to isolate complexity in many small classes. And there is a lot of pressure to pick one of those over the other rather than do the work it takes to respond to both.

Look at your Curves from the point of view of the classes that use them. Design them each a simple interface that describes their need. But feel free to hide as many classes (1 or many) as you like behind those interfaces. If you go for many just work out a simple way to construct their object graph.

Do it right and you can pick one implementation style over the other later. The clients using Curves won't know or care which you do. This frees you to refactor and explore without breaking the rest of the code base.

1
  • hide all those CurveThis CurveThat in kind of UseCaseClass? Commented Jul 11, 2022 at 10:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.