liveBook · Manning

Divide-and-conquer approach

As explained in Functional Design and Architecture: Examples in Haskell

Divide and conquer is a design strategy that manages complexity by separating a problem into smaller, more independently tractable parts.

Divide and conquer manages complexity by separating a problem into smaller, more independently tractable parts. In software design, this means separating interfaces from implementations so clients depend on stable abstractions rather than internal details. section 1.3.2

Apply the approach through several related design moves:

  • Define an API as a contract for operations, inputs, outputs, and types, independent of implementation. section 1.3.2
  • Hide replaceable implementations behind interfaces, allowing client code to remain unchanged. section 1.3.2
  • Divide applications into architectural layers and error domains, giving each error domain an appropriate handling strategy. section 13.3
  • Use low coupling and high cohesion to keep complexity manageable across object-oriented and functional designs. section 1.6

Adapting a thermometer interface

A high-accuracy thermometer exposes Kelvin readings but cannot implement an ISensor interface expecting Celsius. HAThermometerAdapter converts the reading and supplies the expected sensor methods, isolating the mismatch from client code. section 1.4.2

Listing 1.4 Interfaces and inversion of control
final class Receiver extends IConnection {                   #1
  def send(dataType: String, name: String, data: Float) =
    server.connection.send(dataType, name, data)
}
 
final class Thermometer extends ISensor {                    #2
  val correction = -12.5f                                    #3
  def transform(data: native.core.Temperature) : Float =
    toCelsius(data) + correction
 
  def getName() : String = "T-201A"                          #4
  def getDataType() : String = "temperature"
  def getData() : Float = {
    val data = native.core.thermometer.getData()
    transform(data)
  }
}
 
object Worker {                                              #5
  def observeThermometerData() {
    val t = new Thermometer()
    val r = new Receiver()
    val observer = new Observer(t, r)                        #6
    observer.readAndSendData()
  }
}

Trace how ISensor, Receiver, Thermometer, and Observer separate sensing, transmission, and coordination responsibilities. section 1.3.2

How Functional Design and Architecture: Examples in Haskell covers it

How deep it goes
Explains interface boundaries, substitution, dependency injection, adapters, and error-domain conversion, while stopping short of formal algorithmic analysis.
Examples
Uses sensor interfaces, thermometer implementations, an Observer, an IoC wiring example, and a Kelvin-to-Celsius adapter.
What it assumes
Assumes familiarity with software components and client code while introducing interfaces, APIs, inversion of control, and dependency domains.
What you can do after
Apply interfaces, adapters, and dependency injection to isolate implementations and make software more maintainable, testable, and readable.
How it connects
Connects modularity with interfaces, inversion of control, dependency inversion, adapters, coupling, cohesion, and error handling.

Also explained in

Compare how 3 books explain divide-and-conquer approach →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Functional Design and Architecture: Examples in Haskell. The section links point to the parts of the book it draws on.