design – Should we have a class for each responsibility?

Philosophy of this problem.

One of my favorite quotes is:

Everything must be as simple as possible, but not simpler. [1]

This quote captures what we want to achieve when we design a good interface: our interface makes things as simple as possible, without being so simple that they cause problems.

Things to consider when using a function

Here are the questions I have looking at their function:

  • Do we have to copy all the titles of the library into a vector in order to use it?
  • Does each string contain only the title or does it contain additional information about the book (such as the author or date of publication)?
  • How efficient can this search be implemented?
  • What happens if we want to look for something that is not a title?

Things to consider when using an interface

  • Do we have multiple separate implementations that serve a similar purpose?
  • Does the interface provide any benefit over a regular function?
  • Is the interface easy to use for the most common cases?

Proposed architecture

We have a database interface (to communicate with the database of the library) and a query class (which may or may not be an interface, that's up to you). The search is done through the database interface, and the specific search method you want to use is a property of the Query class.

struct LibraryBook {
std :: string title;
std :: string author;
std :: string ISBN;
std :: string publishing_company;
int publication_year;
class consultation; // Base class for a search query

LibraryDatabase {class
virtual void add (LibraryBook const & book) = 0;
virtual void remove (LibraryBook const & book) = 0;
virtual std :: vector search (query and query) const = 0;
virtual ~ LibraryDatabase () {}
DefaultDatabase class: public LibraryDatabase {
// The default implementation goes here
// We can wrap a specific class around you:
library class: public LibraryDatabase {
std :: unique_ptr database;

// factory function
     Static mark of make (Args && ... database_args) {
Return library (new database (std :: forward(database_args) ...));
Library (): database (new default database ()) {}
Explicit library (LibraryDatabase * db): database (db) {}

// The fields go here;
std :: vector search (consultation and consultation) const {
return database-> search (query);
// The fields go here;
void add (LibraryBook const & book) const {
return to the database-> add (book);
void removeLibraryBook const y book) const {
return the database-> delete (book);

[1]: This quote is often attributed to Einstein