lewis
lewis

Reputation: 858

OOP where to put orphan methods

Apologies if this has been answered elsewhere, my search didn't yield quite the answer I was looking for.

Hypothetically speaking, let us say I am building an application for a bookshop.

I have a class that handles all my database transactions. I also have a 'Book' class which extends the Database class, calling the Database constructor from it's own constructor, removing the need to instantiate the Database class first:

class Book extends Database {
    __construct($book_id){
        parent::__construct();
        $this->databaseGet("SELECT * FROM..."); // method in Database class
        etc...
    }
}

I can pass a reference id to the 'Book' class constructor and create an object containing information pulled from the database about that book along with several methods relevant to a given book.

But I also want to list all the books in the database. My question is, where do I put this method and other methods that simply don't have a context such as 'Book'?

I could create a single "GetStuff" or 'Bookshop' class that extends the Database class, which would contain all these single-use methods. But that requires it to be loaded all the time as these orphan methods would be used all over the program.

I could create lots of classes that house a single method but that would require instantiating the class to an object in order to call the method, seems like overkill.

They aren't general utilities, they have a place in the business model. Just where should I put these orphan methods?

Upvotes: 1

Views: 177

Answers (3)

Nazar Merza
Nazar Merza

Reputation: 3454

Seems your design is seriously flawed. You have to separate three concerns:

  1. Your Domain Layer (DM): In this case, Book belongs to it.
  2. Data Access Layer (DAL): Handles database storage. Domain Layer does not know about this layer at all.
  3. Service Layer (SL): handles use cases. A use case may involve multiple object from Domain, as well as calls to DAL to save or retrieve data. Methods in service layer perform a unit of work.

A simplified example:

// Model Object
        class Book {
           title;
           author:
           isbn;
           constructor(title, author, isbn) {// ...}
           // other methods ...
        }


// DAL class
class BookDataMapper {
         // constructors ...

         save(Book book) {}
         Book getById(id) {
           String query = get from book table where book_id = id;
           execute query;
           parse the result;
           Book book = new Book(parsed result);
           return book;
         }
         Book getByTitle(title) {}
         ...
         getAll(){} // returns all books

    }

//Service class 
class BookService {

     BookDataMapper bookMapper;


     buyBook(title) {
        // check user account

        // check if book is available
        Book book = bookMapper.getBytitle(title);

        if book available
        charge customer
        send the book to ship etc.

     }
}

Upvotes: 0

Tony Hopkinson
Tony Hopkinson

Reputation: 20320

Book is a book, Books is collection of books. Database is one thing you could use to persist a lot of books so you don't have to type them all in again.

It could be an xml file, an excel spreadsheet, even a webservice.

So write Book and Books, then write something like

BookDatabase extends database with methods like Books GetBooks(); and void SaveBook(Book argBook); The real trick is to make Book and Books work no matter what / how they are stored.

There's lot more to learn around that, but first thing to do is start again and not make your data objects dependant on a particular "database".

Upvotes: 2

entonio
entonio

Reputation: 2173

If I understand it, you're asking where should code go that relates to a specific type but doesn't implement a behaviour of the type itself. There is no single answer. According to the overall design of the system, it could be part of the type - Smalltalk classes have 'class fields' and 'instance fields', and there is nothing wrong with that - or it could end up anywhere it makes sense. If it relates to something external to the type itself - that is, it's not merely a matter of not being the behaviour of an instance, but a matter of being an interaction with something extraneous - it may make sense to put it outside. For instance, you may have Book, BookDatabase, BookForm, BookWebService, etc. There's no harm in some of those classes having few members, you never know when you'll want to add some more.

Upvotes: 2

Related Questions