5

Every class of a model in my application has a create method. The framework gives me the default implementation of create which is “create in the DB”. Sometimes I need to perform some extra actions during create. There are numerous of ways to achieve this: I can override create (and call super thereafter), subsribe to signals (if possible), override hook methods (such as before_create), it has never been a problem. The problem is, I don't actually want to perform this actions every time, i. e. these actions are not actually part of the create procedure, it's a part of some other high-level (comparing to the original create) procedure. They call it smart_create in one of the biggest projects I've ever worked with.

Before proceeding any further, I want to give you an idea of what these smart_create methods usually are. So, here is the example. When you create some entity for a user, you may want to notify him with an email. But you may not want notifications during tests (it may slow them down), stand-alone scripts (when you repair something you just broke) or when an entity is created via admin panel (for whatever reason). The temptation is there to name this method create_and_notify, but this won't work when you need some other actions: create_and_notify_and_add_bonus_credits is not an appropriate name.

So, what is the question? I don't like to name my methods smart_create, but I can't really tell what name is better. Here is what I tried and why I still don't like it:

  • smart_create. What if I have two untrivial ways of creating entity? What is the name for the second one? wise_create? More than this, how do other developers know what method to call? Do they need to “go smart”?
  • EntityCreator class. Actually the same but with the whole class instead of just one method. Nothing really changes: it's still unclear what this “creator” do and why it's better than bare “create”.
  • No name at all. Because controller knows what to do. That's just dirty. The problem with this approach is clear: I will not be able to reuse the same logic again.
  • create_by_user. Here. This is what method really does, but I don't actually want to mention some users or clients in my models: they are not supposed to know about them at all.

I believe a lot of developers encountered such situations, and I want to know what is your usual approach to this problem.

  • 1
    A callback on completion would be a better architecture. – RubberDuck Feb 15 '16 at 3:11
  • It's the “controller knows that to do” case. – Vadim Pushtaev Feb 15 '16 at 13:49
  • It looks to me like your "smart_create" methods are part of a god-object and a system that is really not well factored. Usually, if the creation of an entity needs to trigger many actions (or includes many validations or any other business rules), this is in fact a use-case, and should be dealt with by its own module or unit of code (package, namespace, class, whatever, but not a method). You can check the single responsibility principle (and the remaining of SOLID) for some reference on this. – MichelHenrich Feb 15 '16 at 16:07
4

My approach is to:

  1. Use a name that indicates what happens, if not precisely, then at least unambiguously, instead of a name that simply gives some vague hint that what will happen will be outside of what might arbitrarily be considered as "the usual". That's because "the usual" tends to vary depending on the module you are working on, the kind of work you are doing, and even the phase of the project. So, names like "smart_create", "special_update", "save_extended", "import_other" etc. are completely out of the question as far as I am concerned.

  2. Use a name that makes sense in the context of the callers of the function, but without burdening the callers with concepts that they do not care (and should not care) about. This means create_by_user might be preferred over create_and_notify, because the fact that a notification will be issued is an implementation detail that the caller probably does not care to know.

However, as you have discovered, there are cases where #2 above cannot be satisfied, because the caller might not want to know anything about users, either. If the name is generic enough as to spare the caller from dealing with entities that they would rather not know anything about, then the name might be too generic to describe what the callee is really doing.

In these cases, what you sometimes need to do is to interject an additional interface between caller and callee, so as to introduce a change of paradigm. This way, the caller is given an interface to work with, knowing nothing about its implementation, and invokes a method on that interface called create, and the caller is happy, because they are not burdened with any entities that they do not want to know about.

The object implementing that interface is selected during system assembly. (The initialization phase during which dependencies are resolved.)

So, under normal circumstances, the standard implementation of that interface will invoke create_by_user, to create the user and issue a notification, but for the admin panel you will probably wire objects differently, using some other implementation of the interface which invokes create_by_admin or simply just_create to refrain from issuing notifications.

7

Just have your create method and pass in some policies that supply additional functionality.

Naming those policies should be easy.

  • 1
    I probably have to repeat some set of policies in several places, so I want to make a preset. But naming that preset is exactly as hard as original methods. – Vadim Pushtaev Feb 14 '16 at 21:48
  • @VadimPushtaev why not name a preset Default? – soandos Feb 16 '16 at 14:59
1

One of the indication that you're violating the Single Responsibility Principle is when you find yourself wanting to name things with multiple 'ands' in the name. create_and_notify_and_add_bonus_credits, etc.

https://en.wikipedia.org/wiki/Single_responsibility_principle

If you're working in an environment which allows an event dispatcher, message hub, hooks, etc, that's a convenient way to separate out some of the concerns, while still binding their execution together at runtime. For instance, the user create would only be responsible for creating the user and dispatching an event or triggering any attached hooks. In your test environment, you're then free to mock up any objects which operate after the execution of the user create method.

For a more extreme approach, you can read up on event-driven architecture: https://en.wikipedia.org/wiki/Event-driven_architecture

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.