DZone
Web Dev Zone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
  • Refcardz
  • Trend Reports
  • Webinars
  • Zones
  • |
    • Agile
    • AI
    • Big Data
    • Cloud
    • Database
    • DevOps
    • Integration
    • IoT
    • Java
    • Microservices
    • Open Source
    • Performance
    • Security
    • Web Dev
DZone > Web Dev Zone > Practical PHP Patterns: Separated Interface

Practical PHP Patterns: Separated Interface

Giorgio Sironi user avatar by
Giorgio Sironi
·
Sep. 15, 10 · Web Dev Zone · Interview
Like (1)
Save
Tweet
1.02K Views

Join the DZone community and get the full member experience.

Join For Free

Before analyzing this pattern, we have to clarify some concepts and introduce some kind of nomenclature.

Glossary

A component, or package, is a set of cohesive classes. In PHP applications, it is usually homogeneous to a folder, or a one of the highest level namespaces. Examples of components are Zend_Auth, sfYaml, Doctrine\DBAL or Doctrine\ORM.

There are also different levels of abstraction for components: Doctrine\ORM\Metadata is a subcomponent of Doctrine\ORM.

If you are reading this article, of course there is no reason to explain the term class.

A dependency (which can be established between classes or interfaces or abstract classes or components) is defined as follows. If A depends on B, in case we delete B from the codebase, A ceases to work, even in its own unit tests. In fact, if we can break the dependency in unit test, than there is not a dependency at all.

The cease to work errors reside at the level of parse errors or unit tests error, where the class is really tested as much as possible in isolation.
For example, if Car depends on AbstractEngine, since it takes it as a collaborator in its constructor, it can't be instantiated in unit tests without AbstractEngine, but it can without a concrete implementation (with a mock - generated or handrolled - for example). If Car extends AbstractCar instead, its source file can't even be included without its dependency.

Corollary

If even only one class (or interface) A from a component P depends on a class (or interface) B from a component Q, the component P depends on the component Q. We can't delete Q without the class A ceasing to work, so that the component P becomes incomplete and not functional (probably not only by name but also by fact, since the other classes may have dependencies on the broken A).

The pattern

The Separated Interface pattern prescribes to define an interface into a package P but implement it in another one Q. The goal of the pattern is one of the most noble in software engineering: breaking dependencies.

In our example, Q depends now on P, but P does not depend on Q. The dependency is now backwards with respect to the classic solution of keeping interface and implementation together.

In fact, this pattern is commonly used to implement Dependency Inversion: if the interface was kept with its implementation, P would have dependended on Q. There are obviously various techniques to realize then instantiation and lifecycle management (such as Dependency Injection or a Service Locator).

When using this pattern, higher-level components do not depend anymore on lower-level ones, but only on their own abstractions, contained in them. Different implementations for the lower-level components can be used.

This approach can be taken further by defining an intermediate packages for the interfaces: P depends on A (Abstraction) by composition, while Q depends on A by implementation or subclassing. Also according to Fowler, this variation comes handy when there is more than one client package (homegeneous to P).

Runtime wiring

At runtime, the software cannot work without implementation for all the used interfaces. However, components can be tested in almost total isolation.
In our example with a stub or a mock for the interface for P. At the same time, with the only dependency of an interface (no code but only method signatures) for Q. Here you have more flexibility because you can swap implementations even in production (not with a mock but with other real implementations) to create new composite behaviors.

Note that the actual the instantation is not a responsibility of each of the interacting components: an higher-level layer with a Factory (used at startup or as a runtime-wired Factory object hanging around ready to instantiate every needed class) or another creational pattern will perform this job.

Principle

The principle that emerges with the creation of Separated Interfaces is that the developers of the client code are responsible for defining the interface they use. The client, and not the implementation, should drive the interface form.

This happens all the time with TDD in the mockist style, where you extract mocks in test code and implement them with concrete classes later: the unit tests define not only the class contract on the user side (which methods it has) but also on the collaborator one (which collaborators and which methods it uses).

While introducing a third intermediate component containing interfaces, this responsibility is moved away from both the client and implementation components.

Issues

This pattern can be an overkill if only one implementation of the lower-level component is needed, or if the division in components is more nominal than real. In the case of three packages, if one of the two sides have no multiple implementations there is really no need for further separation.

Examples

We'll show some UML class diagrams for the situations described in this article.

Here is one for the simplest Separated Interface implementation.

While this is the case with three separate components.

Interface (computing) PHP unit test Implementation Dependency injection

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Low Code and No Code: The Security Challenge
  • 10 Programming Habits a Web Developer Should Embrace
  • Which Backend Frameworks Are Impacting Web App Development Immensely?
  • How to Submit a Post to DZone

Comments

Web Dev Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • MVB Program
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends:

DZone.com is powered by 

AnswerHub logo