About Temporal Coupling:
https://hackernoon.com/api-design-temporal-coupling-2c1687173c7c
https://www.pluralsight.com/tech-blog/forms-of-temporal-coupling
Bonus: How to solve it (see Indecent Exposure and Data Clumps).
class PrivateClassesView: | |
def post(self): | |
# code omitted for readability sake | |
data = { | |
'to': ['[email protected]'], | |
'body': 'blablabla', | |
'scheduled_for': datetime(2019, 3, 30, 11, 1, 0) | |
} | |
if subject: | |
data['subject'] = subject | |
email = Email.objects.create(**data) # database object | |
email.send() # has important logic on email flow | |
class GroupClassesView: | |
def post(self): | |
# code omitted for readability sake | |
data = { | |
'to': ['[email protected]'], | |
'body': 'blablabla', | |
} | |
if subject: | |
data['subject'] = subject | |
email = Email.objects.create(**data) | |
email.send() | |
class IntensiveClassesView: | |
def post(self): | |
# code omitted for readability sake | |
data = { | |
'to': ['[email protected]'], | |
'body': 'blablabla', | |
} | |
if subject: | |
data['subject'] = subject | |
email = Email.objects.create(**data) | |
email.send() |
""" | |
Only this method would know the logic behind Email. | |
Consumers, like our views, shouldn't think about Email implementation details. | |
""" | |
def schedule_email(to, body, subject='My default subject', scheduled_for=None): | |
data = { | |
'to': ['[email protected]'], | |
'body': 'blablabla', | |
'subject': subject, | |
} | |
if scheduled_for is None: | |
data['scheduled_for'] = now() | |
email = Email.objects.create(**data) | |
email.send() |
class PrivateClassesView: | |
def post(self): | |
# code omitted for readability sake | |
schedule_email(to, body, subject, scheduled_for) | |
class GroupClassesView: | |
def post(self): | |
# code omitted for readability sake | |
schedule_email(to, body, subject) | |
class IntensiveClassesView: | |
def post(self): | |
# code omitted for readability sake | |
schedule_email(to, body) |
About Temporal Coupling:
https://hackernoon.com/api-design-temporal-coupling-2c1687173c7c
https://www.pluralsight.com/tech-blog/forms-of-temporal-coupling
Bonus: How to solve it (see Indecent Exposure and Data Clumps).
I don't see the main point here being about getting rid of repetition though. Instead, my understanding is that her point is about encapsulating the logic of how to send an email or, to be more generic, how to make sure we're following the Single Responsibility Principle. The way I see it is that side effects to the main action of a given class/method should belong somewhere else. In her example, sending an email is a side effect of whatever the main classes are supposed to do, thus it's not the responsibility of those classes to know the steps required to send an email.
I do see your point of trying to get rid of code repetition.
However, I think there are pros and cons on both sides. As you know, I am not a fan of too many layers of abstraction. It makes the code harder to read and it's harder to notice side effects. Usually, you'll regret your abstraction by the time you need to implement special cases and suddenly you end up with a lot of optional arguments and
if
else
blocks in your utility function.But I'm usually also not a fan of "developing for the future", I mean like when you write code trying to think of all possible future cases and making it super complex even though you have just a simple problem to solve at the moment. So, it's also arguable to say that if the function
schedule_email
would need to be adapted with special behavior, then it can be refactored by then.In the end, I know that this topic is very opinionated. Some like this, some like that. To be honest, I personally don't see the current code as a "code smell", and I don't follow the DRY principle unless it's business logic. If you're unsure, feel free to ask around what others think.
This article for example is interesting: https://programmingisterrible.com/post/176657481103/repeat-yourself-do-more-than-one-thing-and