Details Pattern

Problem: Over time, code becomes obscured by initialization and cleanup, special cases, and other things.

A solution: Alternate in the program's control flow between primary and details functions. So each "primary function" containing what matters to someone understanding what the code does, is called by a "details function." The details functions take the cruft.

An example, a program to recursively index directories. It started out clear, like this:

indexDirectory addsRef dir = do
contents <- getDirectoryContents dir
mapM_ (\nm -> let name = dir ++ nm in
unless (nm == "." || nm == "..") $ do
b <- doesFileExist name
if b then
index addsRef name
else do
indexDirectory addsRef (name ++ [pathDelimiter])) contents

But it grew over time to be like this:

indexDirectory dir logicalDir = do
contents <- getDirectoryContents dir
mapM_ (\nm -> let name = dir ++ nm in
let logicalName = logicalDir ++ nm in
unless (nm == "." || nm == "..") $ do
b <- doesFileExist name
if b then
maybe
(index name logicalName)
(\f -> do
unpacked <- f name
indexDirectory unpacked (logicalName ++ "@"))
(lookup (takeExtension name) unpacks)
else
do
userdata <- getAppUserDataDirectory "Index"
tmp <- getTemporaryDirectory
unless
(name == userdata || name == tmp || name == "/dev" || name == "/sys")
(indexDirectory (name ++ [pathDelimiter]) (logicalName ++ [pathDelimiter])))
contents

But then by abstracting the details, I got this, which is similar to the original:

indexDirectory dir logicalDir = do
contents <- getDirectoryContents dir
mapM_ (\nm -> let name = dir ++ nm in
let logicalName = logicalDir ++ nm in
unless (nm == "." || nm == "..") $ do
b <- doesFileExist name
if b then
details1 name logicalName {-Primary control flow:-}(index name logicalName)
else
details2 name {-Primary control flow:-}(indexDirectory (name ++ [pathDelimiter]) (logicalName ++ [pathDelimiter])))
contents


EditText of this page (last edited October 9, 2013) or FindPage with title or text search