9

I'm using a large interface with about 50 methods to access a database. The interface has been written by a colleague of mine. We discussed this:

Me: 50 methods is too much. It's a code smell.
Colleague: What shall I do about it? You want the DB access - you have it.
Me: Yeah, but it's unclear and hardly maintainable in the future.
Colleague: OK, you are right, it's not nice. How should the interface look like then?
Me: How about 5 methods that return objects that have, like, 10 methods each?

Mmmh, but wouldn't this be the same? Does this really lead to more clarity? Is it worth the effort?

Every now and then I'm in a situation where I want an interface and the first thing that comes to mind is one, big interface. Is there a general design pattern for this?


Update (responding to SJuan's comment):

The "kind of methods": It's an interface for fetching data from a database. All methods have the form (pseudocode)

List<Typename> createTablenameList()

Methods and tables are not exactly in a 1-1-relation, the emphasis is more on the fact that you always get some kind of list that comes from a database.

  • 12
    There is missing relevant information (which kind of methods you have). Anyway, my guess: if you divide only by number then your colleague is right, you are not improving anything. One possible division criterium would by the "entity" (almost the equivalent of a table) returned (so, a UserDao and a CustomerDao and a ProductDao) – SJuan76 Jun 18 '14 at 11:51
  • Indeed some tables are semantically close to other tables forming "cliques". So do the methods. – TobiMcNamobi Jun 25 '14 at 9:23
  • Is it possible to see the code? I know it is 4 years old and you probably fixed it by now :D But I would love to think about this problem. I solved something like this before. – clankill3r Nov 6 '18 at 20:52
  • @clankill3r Indeed I have no access any more to the specific interface that made me post the question above. The problem is more general though. Imagine a DB with around 50 tables and for each table a method like List<WeatherDataRecord> createWeatherDataTable() {db.open(); return db.select("*", "tbl_weatherData");} – TobiMcNamobi Nov 7 '18 at 9:49
15

Yes, 50 methods is a code smell, but a code smell means to take a second look at it, not that it's automatically wrong. If every client using that class potentially needs all 50 methods, there may not be a case to split it. However, this is unlikely. My point is, splitting an interface arbitrarily can be worse than not splitting it at all.

There isn't a single pattern to fix it, but the principle that describes the desired state is the Interface Segregation Principle (the 'I' in SOLID), which states that no client should be forced to depend on methods it doesn't use.

The ISP description does give you a hint on how to fix it: look at the client. Often, just by looking at a class it seems like everything belongs together, but clear divisions emerge when you look at the clients using that class. Always consider the clients first when designing an interface.

The other way to determine if and where an interface should be split is by making a second implementation. What often ends up happening is your second implementation doesn't need a lot of the methods, so those clearly should be split off into their own interface.

  • This and utnapistim's answer are really great. – TobiMcNamobi Jun 20 '14 at 6:32
13

Colleague: OK, you are right, it's not nice. How should the interface look like then?

Me: How about 5 methods that return objects that have, like, 10 methods each?

That's not good criteria (there is no criteria at all in that statement actually). You can group them by (assuming your application is a financial transactions app, for my examples):

  • functionality (inserts, updates, selects, transactions, metadata, schema, etc)
  • entity (user DAO, deposit DAO, etc.)
  • application area (financial transactions, user management, totals etc)
  • abstraction level (all table access code is a separate module; all select APIs are in their own hierarchy, transaction support is separate, all conversion code in a module, all validation code in a module, etc)

Mmmh, but wouldn't this be the same? Does this really lead to more clarity? Is it worth the effort?

If you choose the right criteria, definitely. If you do not, definitely not :).

A few examples:

  • look at ADODB objects for a simplistic example of OO primitives (your DB API probably already offers this)

  • look at Django data model (https://docs.djangoproject.com/en/dev/topics/db/models/) for a data model idea with a high level of abstraction (in C++ you will probably need a bit more boiler plate code, but it's a nice idea). This implementation is designed with a "model" role in mind, within the MVC design pattern.

  • look at the sqlite API for a flat API idea (http://www.sqlite.org/c3ref/funclist.html), consisting of just functional primitives (C API).

3

Every now and then I'm in a situation where I want an interface and the first thing that comes to mind is one big interface. Is there a general design pattern for this?

It's a design anti-pattern called the monolithic class. Having 50 methods in a class or interface is a probable violation of the SRP. The monolithic class comes about because it tries to be everything to everyone.

DCI addresses method bloat. Essentially the many responsibilities of a class could be apportioned to roles (offloaded to other classes) that are only relevant in certain contexts. The application of roles can be achieved in a number of ways including mixins or decorators. This approach keeps classes focused and lean.

How about 5 methods that return objects that have, like, 10 methods each?

This suggests instantiating all roles when the object is itself instantiated. But why instantiate roles you might not need? Instead, instantiate a role in the context in which you actually need it.

If you find that refactoring toward DCI is not obvious, you could go with a simpler visitor pattern. It provides a similar benefit without emphasizing the creation of use case contexts.

EDIT: My thinking on this has changed some. I provided an alternative answer.

1

It looks to me every other answer is missing the point. The point is that an interface should ideally define an atomic chunk of behavior. That is the I in SOLID.

A class should have one responsibility but this could still include multiple behaviors. To stick with a typical database client object, this may offer full CRUD functionality. That would be four behaviors: create, read, update and delete. In a pure SOLID world the database client would implement not IDatabaseClient but unstead ICreator, IReader, IUpdater and IDeleter.

This would have a number of benefits. First, just by reading the class declaration one would instantly learn a lot about the class, the interfaces it implements tell the whole story. Second, if the client object were to be passed as an argument, one now has different useful options. It could be passed as an IReader and one could be confident the callee would only be able to read. Different behaviors could be tested separately.

When it comes to testing however, common practice is to just slap an interface on a class that is a 1-to-1 replica of the full class interface. If testing is all you care about this may be a valid approach. It allows you to make dummies fairly quickly. But it is hardly ever SOLID and really an abuse of interfaces for a dedicated purpose.

So yes, 50 methods is a smell but it depends on the intent and purpose whether it is bad or not. It is certainly not ideal.

0

The Data Access Layers tend to have a lot of methods attached to one class. If you've ever worked with Entity Framework or other ORM tools, you'll see they generate 100s of methods. I assume you and your colleague are manually implementing it. It's not necessary a code smell, but it's not pretty to look at. Without knowing your domain, it's hard to say.

  • Methods or properties? – JeffO Jun 18 '14 at 13:31
0

I use protocols (call them interfaces if you want) almost universally for all apis with both FP and OOP. (Remember the Matrix? There are no concretions!) There are, of course, concrete types but within the scope of a program every type is thought of as something which plays a role within some context.

This means objects passed throughout programs, into functions, etc. can be thought of as abstract entities with named sets of behaviors. The object can be thought of as playing a role which is some set of protocols. A person (concrete type) could be a man, a father, a husband, an friend and an employee but I can't imagine many functions that would consider the entity the sum of more than 2 of those.

I guess it's possible that a complex object could abide a number of different protocols, but you'd still be hard pressed to get to a 50-method api. Most protocols have 1 or 2 methods, maybe 3, but never 50! Any entity that has 50 methods should be made the aggregate of a bunch of smaller components each with its own responsibilities. The entity at large would present a simpler interface that abstracts away the sum total of the apis within it.

Rather than thinking in terms of objects and methods, start thinking in terms of abstractions and contracts and what roles a subject plays in some context.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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