简体   繁体   中英

Is it appropriate for a Factory class to also include functionality of extracting data from a database

One of the main aspects of software development that I struggle with is delegating the correct responsibility into classes within my program. Working at my first junior role I'm also being exposed to a lot of different design patterns and ideas, and sometimes the information can be overwhelming.

Obviously when we are building software we tend to state that a class should be responsible for one thing and one thing only. It should do that thing well and nothing more. So in the case of the Factory pattern the Factory class should be responsible for building the product and exposing an interface that allows the director to extract the product from the factory.

However the factory class obviously needs to receive the data to build the product from somewhere, without input data we have no output product. Therefore I'd like to know whether including functionality for a factory to query a database is appropriate? My rationale for this is that if the factory is tasked with building a particular product, then it should also be responsible for retrieving the data required to build that product. But I'm not 100% sure if this is correct.

Alternatively, should there be a repository class who's responsibility is to retrieve the data in question from the database, which can be then passed to the factory for assembly into the required product? The use of a repository class seems a bit excessive in this case as we have a class that will hold a large number of different pieces of data which then must be shipped into the factory class.

If we also bear in mind Uncle Bob's teachings that state that functions and methods should have absolutely no more than three parameters then we will be breaking this rule by passing in a large amount of data to the factory. If we first assemble the data into an encompassing class before passing to the factory then we are essentially doing the factory's job within the repository class.

Some guidance would be really appreciated on this, as in my head the lines are very blurry and I'm not sure how I should proceed.

You shouldn't use the factory pattern to object building that extracted from a database. There are the Repository pattern and the Data Mapper pattern for this goal. Those patterns must encapsulate all logic of work with the data storage. Those patterns must have the following responsibility:

  • the Repository must give an interface to business logic for work with data storage
  • the Data Mapper must convert data from database to concrete object

The algorithm of cooperation between objects can look like:

  • business logic uses a repository to read/persist objects.
  • the repository uses a Data Mapper to convert objects to INSERT or UPDATE queries and to convert data from data storage to object

Also, you can read more details about the repository pattern in C# on the site of Microsoft and you can see C# example of the repository pattern

Use 2 different classes.

A Data Access Object (DAO) provides an abstract interface to the database, and hides its details.

A factory abstracts and hides the details of the creation of your objects. For example, for unit testing you might want to configure the factory so that it doesn't use the Database at all.

To reduce the number of parameters between the DAO and the factory, wrap your many pieces of data in a few logically related classes.

Is it apropriate for a Factory class to also include functionality of extracting data from a database

My rationale for this is that if the factory is tasked with building a particular product, then it should also be responsible for retrieving the data required to build that product. But I'm not 100% sure if this is correct.


A product to retrieve from database is not a trivial object, it is a domain model . A domain model (aka business model, aka entity(which might indicate a particular instance of it)) belongs to your domain layer (aka business layer). In this regard, there are some patterns you should be a minimum familiar with...

( Active Record ) VS ( Data Mapper + Repository ) VS ( Table Data Gateway + Factory)

Active record pattern kind of violate the Single Responsibility Principle by leading you to implement database access logic inside your domain model and tightly couples them.

Ideally, to avoid the cons above for the cost of a slightly increased complexity (on short term only), we separate the database access logic into a supplementary layer, the data access layer . One of the main component of this layer being the data mapper which in our context (READ operation) is in charge to retrieve data from database and map it to a new domain model instance, your specific product (entity). More generally it encapsulates CRUD operations to the database abstracting this database. Its API inputs and outputs are entity objects and possibly Query Objects .

Optionally, a featured data mapper would make use of patterns such as:

  • Unit Of Work
  • Lazy Loading
  • Identity Map
  • Transaction
  • Lock Strategies
  • Metadata Mapping

Alternatively, should there be a repository class who's responsibility is to retrieve the data in question from the database, which can be then passed to the factory for assembly into the required product? The use of a repository class seems a bit excessive in this case as we have a class that will hold a large number of different pieces of data which then must be shipped into the factory class.

Repository is not a part of your data access layer, but of your domain layer. So it is client of your data access layer. It doesn't encapsulate any database access logic but uses the data mapper.

A repository encapsulates query logic for a particular domain model plus a collection of in-memory entities you've previously retrieved. A very basic example:

class ProductRepository
{
    private $productCollection;

    public function findById($id)
    {
        if (!$this->productCollection->has($id)) {
            $product = $this->dataMapper->get(new Query(Product::class, $id));
            $this->productCollection->add($product);
            return $product;
        }

        return $this->productCollection->get($id);
    }
}

Finally, we can encapsulate database access logic in a table data gateway and use it in a factory. This would result in a solution similar to Gonen I's one . It is simple to implement but there might be cons compared to the data mapper solution. I've never implemented, used or even studied this approach so I can't tell much...


You'd definitely learn a lot by attempting to implement all that by yourself and I'd encourage you to, but keep in mind that if you need a serious solution, ORMs might be interesting for you.

If you're keen to learn more about all this, I recommend Martin Fowler's Patterns of Enterprise Application Architecture book which is summarized here: https://www.martinfowler.com/eaaCatalog/index.html

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM