2

I am having difficulty understanding which design pattern would be best for the following scenario. An application uses a database. That database can either be the production version (SQL) or a test version (Test). Here's my current design:

enter image description here

The problem with this design is that application has to create Test, which it shouldn't be doing (since it's purely for testing). However, Application must somehow get reference to the database it's going to use, either SQL or Test.

What design pattern could I implement to fool application into creating the Test version of the database? How would this solution look in UML?

I have already done enough research to narrow my choices to Abstract Factory, Builder, or Factory Method (or the combination of any)

3
  • 1
    Sorry about that, I was (and am) worried about running afoul of our code of conduct. But you’re right, I could at the very least provide feedback. This question... misses the point. I mean, how do you choose between one database or another? That is an if-statement, not a uml driven design pattern. Sure, an if-statement is usually a bad solution to this sort of problem, but it is the simplest thing that works. You don’t explain why this obvious simple solution is insufficient. Or why a basic config value would be insufficient. Or why dependency injection is insufficient. Or...
    – Telastyn
    Commented Nov 17, 2018 at 6:13
  • @Telastyn Read the question a little closer and you'll find that it is not a decision between two databases. The problem is what class creates the database object. I don't think you understand the point of a design pattern. It's so much more than a simple if statement. Think of the IDatabase, SQL, and Test being packaged together by your programming team. You might want Test to be created, but it should have to be explicitly created by the client. Also, I can't go through every reason why something is insufficient. There are just too many answers that are wrong, to explain why each is wrong.
    – Corey P
    Commented Nov 17, 2018 at 15:11
  • 1
    Dude, I have been programming for 30 years. Your application needs to choose between SQL and Test. Somewhere there is a decision being made. Maybe you wrap that in a class and call it a Factory. Maybe you wrap that in a smaller class and call it a Strategy. Maybe your build does it when it sets config variables. Maybe you just have two consumers that inject different things. But again, this is missing the point. Programming isn’t patterns and uml. Certainly not for this sort of thing, which shows up thousands or millions of times in a program.
    – Telastyn
    Commented Nov 17, 2018 at 16:26

5 Answers 5

4

When you want a part of your program to instantiate a certain subclass, but hide the instantiation behavior from other client code (your Application class), you should probably go with the Factory Method DP.

That relates to the instantiation part. Regarding the actual behavioral differences between the subclasses, the Strategy DP may be a good solution, as stated by @Paul.

2
  • While this isn't the most in-depth answer, it is the correct one. The factory pattern's entire point is to "Define an interface for creating an object, but let subclasses decide which class to instantiate."
    – Corey P
    Commented Nov 17, 2018 at 15:18
  • @Corey: Note that Factory Method Pattern is slightly different from what people usually call Factory (which isn't described in the Go4 book as such). Regardless, it doesn't quite solve your problem by itself, because now, instead of referencing the concrete implementations of IRepository, you have to reference the concrete implementations of the Factory Method (which are in turn coupled to the corresponding repository), so you've just introduced an intermediary which does nothing to fight the coupling, and increased the complexity of the code. You pay a price, but get nothing out of it. Commented Nov 17, 2018 at 15:29
4

Before I get to the gist of your question, let me point out that the structure you provided has certain problems (which you have noticed, but let me elaborate a bit):

enter image description here

This design uses dependency inversion to make the client (Application) and a concrete service (a DB gateway - SQL, Test) independent of each other, by making each depend on the IDatabase interface. However, the fact that Application creates the concrete service makes it dependent on the concrete type, which to some extent messes up the whole setup. The degree to which this is actually a problem depends on thinks like: what the client code does, developer discipline (in terms of isolating the coupling to a well defined piece of the Application class), techniques used (e.g. the picture changes a bit if the concrete service is instantiated through reflection), how you want to organize your code into libraries, etc.

Relying just on the traditional OO techniques, the way to make sure that client code has no knowledge of the concrete types that implement IDatabase is to introduce a third piece of code that knows about all of these types, and is therefore able to hook them up (and this, in turn, enables these types to stay decoupled). In approaches that use dependency injection, this third piece of code will usually be the composition root (more or less, the entry point of the application).

One way to change the original class diagram you posted so that it follows this structure is to split the Application class into two parts. One part will be the composition root (the ting that does creating and hooking up, and runs the other part, or otherwise enables it to run). The other part will be the client code that uses IDatabase.

This enables you to test this other part (the actual business logic contained in the client code) in isolation (e.g., unit testing) by supplying a concrete database instance in the test code via constructor injection.

The way I described it so far, when it comes to creating concrete instances, the composition root essentially uses "code as configuration", but if you want to (or need to), you can make it truly configurable by having it read a config file and make its decisions based on that data, or you can rely on a DI container to manage the configuration, dependency injection and object lifecycle for you.

3

So, the short answer to your question is that the pattern you're trying to find is probably the Strategy Pattern, as you're interested in selecting an implementation at runtime. Especially if the implementation in your code is very different (eg your real DB is an RDBMS but your test DB is just flat files of test data)

But having said that, I think it's a mistake to try and think of your software in terms of what design patterns it needs. For the most part, you should start with the simplest possible approach and refactor to a pattern when your simple approach isn't good enough.

What do I mean?

Well, let's take your case. In the simplest version, I'd argue that all you need to do is change the DB connectionstring based on environment (prod or test). So, put the connection info in an environment variable and be done with it.

If you find that you need more changes, let's say you need different seed data. Then, either add a function to your test that runs before the test logic to update the content or maybe here you have a seed data file that you select based on another env var.

If you need yet more differences, there's a good chance your design is bad because why would you spend so much time test stuff that's different than what you're doing in production?

Ok, I tricked you. There's actually very little reason to ever use the Strategy Pattern to solve this problem. But it is the one you were "looking for" with how you asked the question. See what I mean?

2
  • thanks for your answer. While my approach may seem complex, in reality it really isn't. It's starting with a simple watered-down design. UML is a great tool to start simple and it's very good practice to implement design patterns, even in the simplest of designs. Design patterns help code become more stable, scalable, and easier to maintain. It's important to implement them early, so you don't have to go back and implement them once the project grows large.
    – Corey P
    Commented Nov 17, 2018 at 15:22
  • @Corey, I appreciate your position; I used to feel the same way until I started seeing g how much of it was just useless bloat that was actually slower to implement and didn't provide the theoretical benefits in practice. But everyone has a different journey, I wish you the best.
    – Paul
    Commented Nov 17, 2018 at 15:33
3

In your design, the application creates its database. However, the application does not have the necessary information to choose the database. Instead, pass a database to the application.

For example, in your application startup code:

Database db = new MySQLConnection(...);
Application app = new Application(db);
app.run();

But in your test code:

Database db = new TestDBConnection(...);
Application app = new Application(db);
app.run();

That is, we inject the dependency into the application, using the Database interface.

Instead of manually injecting dependencies through constructor arguments or function arguments, you could also use a dependency injection framework. Then, the application would request a Database object from the DI framework, and we would have previously configured the DI framework to use a specific class to implement the Database interface. However, use of a framework is not required, and constructor injection works just fine for most projects.

1

Here's the UML to complement the other answers:

Factory

There are at least three variants of the Factory pattern, e.g., Static, Concrete, Factory Method, and Abstract Factory. Static won't really work with an Interface, so Concrete would be adequate if all you want to do is not have Application know about creation details. Factory Method and Abstract Factory don't seem to be appropriate for the situation you've given in the question.

When Application uses a Factory, Application still decides which Database is instantiated, even if the details of how the database is instantiated are hidden in the Factory.

Application use Factory to create the instance

Dependency Injection

Application does not decide which Database is instantiated; some other class, e.g., Decider, does. Decider may use a factory to insulate it from the instantiation details (as above).

Another class decides which implementation and passes it to Application

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.