What is the One Class, One Responsibility Principle?

后端 未结 5 1994
广开言路
广开言路 2021-01-12 22:14

I would like to learn about the One Class, One Responsibility principle. I have found some articles about it, but without examples. It would help me if you can giv

相关标签:
5条回答
  • 2021-01-12 22:29

    This is one of those things where you don't know you need it until you try to do without it and see how it turns into a mess:

    Let's say you write your own Logger utility, and because it starts out simple (a singleton with methods that write to stdout), you decide to put all its functionality into one class. As you use it you think of more features to add:

    • different destinations to log to (stderr, a file, a queue, whatever)

    • ability to change formatting for messages

    • providing separate loggers for different categories

    • ability to filter messages for a category by log level

    • you want to be able to change logging levels while the program is running

    • new logging levels (ERROR, WARN, INFO, DEBUG, etc.)

    etc., and you add the implementation for every feature to this same class you started with. Every change you make means going back to the same class, sorting through it to see what's relevant, then making your changes. The class is going to have different lifecycle events where each of these features gets initialized, so all the feature code isn't in one place, it's spread across different methods in the class.

    Because all the code is crammed together in one class, and methods contain code that implements different features, tracking down all the relevant parts for your change gets challenging, and there's always the possibility a change might inadvertently impact some other feature, so that some previously-existing feature might now stop working in some circumstance. So you have to test all the features in the class, and worse, all the combinations of all the features, in order to catch these errors. Which means the testing will be incomplete and errors can sneak in.

    Now look at how a real project like log4j handles this stuff. There's a separation of concerns where different classes handle formatting, handing how the output gets written, providing a logger for a category, and the interaction between all these pieces is well-defined, with the result that when you tinker with or replace one part it doesn't affect the other pieces. Users can create their own classes that add functionality that they need (where they don't need to know everything about the log framework, they only need to know the contract for that specific kind of piece that they want to add) and plug it in, and users can mix and match different plugins. If a plugin is broken, that breakage doesn't extend beyond that plugin, and changes to individual parts don't threaten the integrity of the whole project. To the extent that contracts govern the interaction of the pieces, you don't need to test all the different combinations of specific features. And it works that way because it's composed of pieces that each have a single responsibility.

    With the single class approach you could never do that, every bit of new functionality somebody added would be a fork of the project, and merging each back in would be a progressively bigger task. The point of Single-Responsibility Principle and the other SOLID rules is to provide an overall strategy that enables changing software in a controlled way without breaking things.

    0 讨论(0)
  • 2021-01-12 22:39

    The principle is exactly what it says it is, not much more. One class should only have one responsibility. It can be difficult to define responsibilities though. An example could be a "DatabaseHandler"-class that handles all database requests in an application.

    Further reading: cohesion.

    0 讨论(0)
  • 2021-01-12 22:43

    On the principle's aspect of not mixing responsibilities.

    If you have an InvoiceProcessor class that handles an invoice, does calculations by business rules, generates a PDF with the invoice, processes the database with registering extra bonus points to the seller, then you have a clear case of needing separation of concerns. In this case a clear separation or even delegation to other classes is due.

    But One Class - One Responsibility is even more subtle and hideous. If you have to provide a solution to invoice processing, and have several goals to fulfil like those bonus points, you can have a violating function that serve several goals: calculate bonus points for seller and discount of customer.

    On a smaller scale: if you have a class with its data structures, like a priority queue or whatever, and at the same time mix it with data structures for caching a part, using say a List and a Map, then you are at some spots manipulating data for different concerns, maybe even addressing inside the cache list, so that the data structures become intertwined. If you then have a function that changes priority and alters the caching state it will get difficult to understand the processes in some future.

    I often encounter violations where some different aspects need to be implemented, and they are done in one class. The API then has calls with different abstraction levels, or a difficult semantic.

    0 讨论(0)
  • 2021-01-12 22:52

    I'm not a 100% expert in this design pattern, but here's how I think of it - if I create an object, it is responsible for exactly one thing. If it needs to do something else, but is related to another object, depending on the situation, I would use inheritance, or interfaces.

    It's a concept that seems fairly simple; make sure that a specific object (or method, for that matter) handles one piece of logic. If it handles more, you need another object (or method).

    0 讨论(0)
  • 2021-01-12 22:53

    Every thing in a class should be related to what the class should be responsible for.

    If it is a class named MailSender, it should only know how to send the mail. Code that creates the mail content should not be inside it.

    0 讨论(0)
提交回复
热议问题