[LRUG] Objects that are neither classes nor records but something in between
Alberto Fernández-Capel
afcapel at gmail.com
Tue Jun 22 08:27:54 PDT 2021
>
> And, in general, i'd be interested to know - is there a name for this
> type of (anti-)pattern - Types of things that refuse to sensibly sit in the
> domain of software classes or Database fields?
What you describe sounds a lot like an anemic domain model
<https://martinfowler.com/bliki/AnemicDomainModel.html>.
>From Martin Fowler's article:
The fundamental horror of this anti-pattern is that it's so contrary to the
> basic idea of object-oriented design; which is to combine data and process
> together. The anemic domain model is really just a procedural style design,
> exactly the kind of thing that object bigots like me (and Eric) have been
> fighting since our early days in Smalltalk. What's worse, many people think
> that anemic objects are real objects, and thus completely miss the point of
> what object-oriented design is all about.
-- Alberto
On Tue, Jun 22, 2021 at 3:48 PM Tim Cowlishaw <tim at timcowlishaw.co.uk>
wrote:
> Hi there folks! Hope you're all doing well.
>
> I've just run into a problem / conundrum / anti-pattern / code smell that
> seems to recur fairly frequently in projects I've worked on of late, and
> which i'm not really sure how to name in order to search for an existing
> solution, so i figured I'd post it here in case anyone has some sensible
> advice, or in the hope that it provokes some interesting discussion, at
> least.
>
> The problem basically arises when there's some 'object' in my system
> (using this word in the vaguest possible sense for now) which has both
> *data* (so fields that can be updated through an API or webapp, and which
> should probably be stored in a database row), but also has a one-to-one
> correspondence with a specific bit of behaviour, expressed as code.
>
> An example:
>
> I'm currently working on a project which is a digital library. It
> contains lots of Books:
>
> class Book < Struct.new(:id, :title, :author, :content); end
>
> Users can buy access to these books either individually (all books are the
> same price), or by buying a subscription to the whole library. Forgetting
> the DB / ORM side of this for a sec, i'd model a toy implementation of what
> i want to do something like the following:
>
> class User
> def initialize(purchases=[])
> @purchases = purchases
> end
> attr_reader :purchases
>
> def has_access_to?(book)
> purchases.any? { |p| p.grants_access_to?(book) }
> end
> end
>
> class Purchase < Struct.new(:product)
> def grants_access_to?(book)
> product.grants_access_to?(book)
> end
> end
>
> class Product
> def self.price
> raise NotImplementedError
> end
>
> def self.name
> raise NotImplementedError
> end
>
> def grants_access_to?(book)
> raise NotImplementedError
> end
> end
>
> class OneOffPurchase < Purchase
> def initialize(book)
> @book = book
> end
> attr_reader :book
>
> def self.price
> 500
> end
>
> def self.name
> "A single book"
> end
>
> def grants_access_to?(other_book)
> book.id == other_book.id
> end
> end
>
> def Subscription < Purchase
> def self.price
> 500
> end
>
> def self.name
> "All inclusive special subscription"
> end
>
> def grants_access_to?(book)
> true
> end
> end
>
> This all to me looks reasonably sensible so far, but i also want those
> product names and prices to be editable by site admins through our
> 'backoffice' webapp, so they need to be stored in the DB, and this is where
> I have trouble finding a solution i'm happy with. Naively I could have an
> ActiveRecord type object and a fairly simple dispatch mechanism to the code
> that works out the permissions, but this has some fairly obvious flaws:
>
> class Product < Struct.new(:unique_key, :name, :price, :book)
> def grants_accesss_to?(other_book)
> if unique_key = :one_off && book.present?
> book.id == other_book.id
> elsif unique_key == :one_off
> raise "oops! we're in a totally inconsistent state
> elsif unique_key == :subscription && book.present?
> raise "yep, this makes absolutely no sense either"
> elsif unique_key == :subscription
> true
> else
> raise "this isn't even a real product type. hopeless."
> end
> end
>
> aside from all the brittleness and smells that you can see above, this is
> also kinda useless in a bunch of respects - we can't add new product types
> without making a change to the codebase, we have to do a bunch of
> convoluted validation to avoid all the various possible inconsistent states
> we can get into above, and our codebase is tightly coupled to the value of
> that unique_key database field, which i'm very suspicious of?
>
> Therefore, anyone got any smart ideas about how to do this better? The
> requirements I'm looking to fulfil, in summary:
>
> 1) A product has a specific 'strategy' for granting access to a book for
> a user, expressed as ruby code
> 2) A product has a price and name that is editable as data through our web
> app's admin interface
>
> And, in general, i'd be interested to know - is there a name for this
> type of (anti-)pattern - Types of things that refuse to sensibly sit in the
> domain of software classes or Database fields? Does any of this even make
> any sense? I'm not so sure myself anymore.
>
> Any thoughts gratefully received!
>
> Cheers,
>
> Tim
>
> _______________________________________________
> Chat mailing list
> Chat at lists.lrug.org
> Archives: http://lists.lrug.org/pipermail/chat-lrug.org
> Manage your subscription: http://lists.lrug.org/options.cgi/chat-lrug.org
> List info: http://lists.lrug.org/listinfo.cgi/chat-lrug.org
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.lrug.org/pipermail/chat-lrug.org/attachments/20210622/520786b2/attachment.html>
More information about the Chat
mailing list