Skip to content

Instantly share code, notes, and snippets.

@Carlsson87
Last active March 29, 2022 11:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Carlsson87/b47818f6c75d3cfaeda74d23812ff792 to your computer and use it in GitHub Desktop.
Save Carlsson87/b47818f6c75d3cfaeda74d23812ff792 to your computer and use it in GitHub Desktop.
Interface Segregation Principle

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).

Coupled.elm

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.

NotSoCoupled

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.

NotSoCoupled2

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.

module Account expsoing (Account)
type alias Account =
{ id : Int
, name : String
, balance : Money
, instruments : List Instrument
, otherStuff : OtherStuff
}
module Coupled exposing (view)
import Account exposing (Account)
import Money
view : String -> List Account -> Html Account
view extraClass accounts =
Html.div
[ Attr.class "account-list", Attr.class extraClass ]
(List.map viewAccount accounts)
viewAccount : Account -> Html Account
viewAccount ({ id, name, balance } as account) =
Html.div
[ Attr.id (String.fromInt id)
, Events.onClick account
]
[ Html.text name
, Html.text " - "
, Html.text (Money.toString balance)
, Html.text " kr"
]
module NotSoCoupled exposing (view, Row)
type alias Row msg =
{ id : String
, onClick : msg
, text : String
}
view : String -> List (Row msg) -> Html msg
view extraClass accounts =
Html.div
[ Attr.class "account-list", Attr.class extraClass ]
(List.map viewAccount accounts)
viewAccount : Row msg -> Html msg
viewAccount { id, text, onClick } =
Html.div
[ Attr.id id
, Events.onClick onClick
]
[ Html.text text ]
module NotSoCoupled2 exposing (view, Config)
type alias Config a msg =
{ id : a -> String
, onClick : a -> msg
, text : a -> String
, extraClass : String
}
view : Config a msg -> List a -> Html msg
view config xs =
Html.div
[ Attr.class "account-list", Attr.class config.extraClass ]
(List.map (viewAccount config) xs)
viewAccount : Config a msg -> Html msg
viewAccount { id, text, onClick } x =
Html.div
[ Attr.id (config.id x)
, Events.onClick (config.onClick x)
]
[ Html.text (config.text x) ]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment