Last active
August 29, 2015 14:20
-
-
Save blaix/3ad853a2b496ac1fb59f to your computer and use it in GitHub Desktop.
Does a @Property with side effects mean "use a method"? I say no, but it does tell us there's a problem with the design.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Let's say we have a service class: | |
class Service(object): | |
def get_article(self, id): | |
return requests.get('/articles/' + id).json | |
# For the model, something like this is what we've been doing: | |
class Article(object): | |
def init(self, id) | |
self.id = id | |
self.data = {} | |
@cached_property | |
def title(self): | |
if not self.data: | |
self.data = Service().get_article(self.id) | |
return self.data.get('title') | |
# I agree with you now that the above is a bad design. `article.title` should | |
# not have side effects (including network calls). But the argument I've been | |
# hearing from you is that this means we should use a method and not a | |
# property. And I don't like that distinction at all. I am thinking about my | |
# interface and I think Article.title should absolutely be a property. But | |
# you're right that there's a smell here, it shouldn't require a network hit. | |
# So I need to rethink my design. I think something like this would be more | |
# appropriate: | |
class Article: | |
def init(**data): | |
self.data = data | |
@classmethod | |
def get_by_id(id): | |
data = Service().get_article(id) | |
return Article(**data) | |
@property | |
def title(): | |
return self.data.get('title') | |
# We've introduced a factory method (`get_by_id`) which returns Article objects | |
# initialized with service data. It's entirely appropriate to expect it to have | |
# side-effects. So this technically does follow the "properties should not have | |
# side effects" rule. But I still don't like the bluntness of that. I don't like | |
# that as an answer to "when should I use a @property vs a method?" I think it | |
# distracts from larger design decisions. We should think about our interfaces. | |
# Does this attribute feel like simple data access? Make it a @property. "Ok but | |
# I wrote it and it it's requiring a service call which is slow / raises | |
# exceptions / whatever". Ok, continue using a @property, but rethink you're | |
# design. It sounds like there's work that needs to happen which should not be | |
# the responsibility of your attribute. Use good OO techniques to come up with a | |
# better design. | |
# Which sounds a lot like what you said actually... |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment