[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