Genom att programmera mot ett gränssnitt/interface möjligör man att koden på båda sidorna kan utvecklas separat så länge båda sidor uppfyller de krav som gränssnittet ställer. Här är ett krystat exempel med 3 varianter av samma "komponent" (filerna finns längre ner).
Används såhär:
Coupled.view "mt-12" model.accounts
|> Html.map AccountWasClicked
Denna modulen importerar Account, den är tight kopplad till den konkreta typen Account.Account
. Den går inte att använda med någonting annat än Account
. Om Account.Account
uppdateras/ändras så behöver eventuellt även modulen Coupled
uppdateras ändras. Detsamma gäller det indirekta beroendet på Money
.
Används såhär:
NotSoCoupled.view "mt-12" (List.map accountToRow model.accounts)
accountToRow : Account -> NotSoCoupled.Row Msg
accountToRow account =
{ id = String.fromInt account.id
, onClick = AccountWasClicked account
, text = String.join " " [ account.name, Money.toString account.balance, "kr" ]
}
Denna modulen har definerat ett API som konsumerande kod förväntas uppfylla. Det medför en större börda på den konsumerande koden att implementera den del av koden överbryggar glappet mellan den konkreta typen, Account
, och det gränssnitt som komponenten definerat, Row msg
. Detta kommer med fördelen att man kan utveckla NotSoCoupled
utan kännedom om den konreta typen, vilket underlättar testbarhet och lokal utveckling.
Används såhär:
NotSoCoupled2.view config model.accounts
config : NotSoCoupled2.Config Account Msg
config =
{ id = .id >> String.fromInt
, onClick = AccountWasClicked
, text = \account -> String.join " " [ account.name, Money.toString account.balance, "kr" ]
, extraClass = "mt-12"
}
Detta är en variant på NotSoCoupled
som är i stort sett likvärdig.