Interface och Factory - Varför så mycket?

Permalänk

Interface och Factory - Varför så mycket?

Jag försöker lära mig hur att programmera som ett proffs. Min åsikt är att programmerare som är väldigt erfarna, kan läsa andras kod och förstå den exakt direkt.

Själv har jag problem med att läsa andras kod, om den är skriven av ett proffs. Är den skriven av en nybörjare så har jag oftast lättare att läsa koden. Detta ger mig känslan att jag själv är nybörjare, än fast jag har programmerat 5 år i sträck och bygger olika projekt hela tiden för att hitta nya metoder för att utveckla mer pedagogisk kod.

Jag är väldigt tydlig att kod ska vara pedagogisk och jag låter hellre koden vara "fet" än att man skriver allt på en enda rad.
Kommentarer används bara när namnen på variablerna, funktionerna, klasserna ej kan beskriva vad som händer.
Jag återanvänder alltid andras kod, dvs jag använder ofta bibliotek skriven av andra, för att minimera mitt kodskrivande. Desto mindre kod jag skriver med ändå löser problemet, desto bättre blir min kod.

Men gång på gång stöter jag på folk som har Interface och Factory exakt överallt i sin kod och jag begriper inte varför dom har det exakt överallt. Jag kan förstå att om man har en abstrakt klass som innehåller exakt allt, sedan vill man "filtrera" bort metoder och flät och skapa ett helt nytt objekt. Då passar interface bra. Men enligt mitt tycke så är arv ett riktigt dåligt sätt att programmera med, om det är inte absolut nödvändigt.

Ibland ser jag många använder Factory och jag begriper inte varför man ska ha det. Kunde man inte lösa detta på något annat sätt?

Citat:

Inom klassbaserad programmering är fabriksmetod (factory method på engelska) ett designmönster som använder fabriksmetoder för att hantera problemet med att skapa objekt utan att ange den exakta klassen för objektet som ska skapas.

Ja, exakt. Utan att ange.

https://sv.wikipedia.org/wiki/Fabriksmetod

Är jag ensamen om detta, eller känner ni också att det är svårt att läsa andras koder? Vad kan det beror på? Dåliga programmerare, eller dålig kod?

Permalänk
Medlem

Interface är inte arv utan ett kontrakt som implementeras av en klass.
Om du kodar mot Interface istället för en konkret typ kan du enkelt byta ut implementationen utan att övrig kod märker det, d.v.s. loose coupling istället för tight coupling (vilket är oönskat).

Eftersom att du kan implementera flera Interface men endast ärva en enda klass möjliggör detta även skapandet av s.k. kompositklasser (se Composition over inheritance).

Factory används också för att hålla koden löst kopplad genom att du inte behöver binda upp en klass till att använda en viss konkret typ. Din klass Vehicle kan istället säga att den har en Motor, men låta Factory returnera den konkreta typen, t.ex. ElectricMotor eller GasolineMotor. Jag tycker att det förklaras ganska bra på den engelska wikin.

Visa signatur

AMD Ryzen 7 1700X 3.8 GHz 20MB | ASUS PRIME X370-PRO | MSI GeForce GTX 1080 Gaming X 8GB | G.Skill 16GB DDR4 3200 MHz CL14 Flare X | Corsair RM650x 650W

Permalänk
Medlem

I många språk hjälper också interfaces en när man behöver mocka implementationer vid enhetstestning. Factories är väldigt vanligt både vid composition eller abstrakta klasser.

Just det där grejerna börjar man fundera på när man börjar mogna som programmerare. Om man inte har mentorer som kan hjälpa en bena ut det är det dags att börja plöja lite böcker

Permalänk
Medlem

Själva Design Patterns boken för ganska bra argument för både Interface segregation (i inledningen) och sen för både Abstract Factories och Factory Methods. Den boken finns att ladda ner från Github i olika format.

Ponera att du vill ha ett program som öppnar en ruta på skärmen, fyller den med grafiska element och gör något. Om du överallt i din kod refererat till interfaces som uppfyller kontrakten för rutor och grafiska element istället för klasser som implementerar det och att du har ett factory för att instansiera dessa objekt så kan du göra det lätt för dig själv att byta vilka klasser som faktiskt implementerar detta beteende.

Tänk dig att du vill att denna koden ska fungera på mac och windows, två olika miljöer. Din kod beror inte på vilka klasser som egentligen utför grafiken, men dessa klasser borde ha olika implementation beroende på operativsystemet. Om du använt ett Abstract Factory så kan det räcka att enbart ändra vilket factory-objekt som skickas till koden beroende på operativsystem. Så din generella kod tar emot ett GraphicsFactory, vilket kan vara ett WindowsFactory eller MacFactory. Vilket då skulle behöva vara den enda skillnaden. Det finns många tillfällen då det kan vara skönt att byta ut hela familjer med relaterade klasser, vilket man kan göra om de alla har ett gemensamt interface (behöver inte strikt vara ett interface, men det kan vara svårt att få en bra arvshierarki annars).

Visa signatur

i5-7600k . GTX 1080 . 16 GB

Permalänk
Medlem
Skrivet av heretic16:

Jag försöker lära mig hur att programmera som ett proffs. Min åsikt är att programmerare som är väldigt erfarna, kan läsa andras kod och förstå den exakt direkt.

Själv har jag problem med att läsa andras kod, om den är skriven av ett proffs. Är den skriven av en nybörjare så har jag oftast lättare att läsa koden. Detta ger mig känslan att jag själv är nybörjare, än fast jag har programmerat 5 år i sträck och bygger olika projekt hela tiden för att hitta nya metoder för att utveckla mer pedagogisk kod.

Jag är väldigt tydlig att kod ska vara pedagogisk och jag låter hellre koden vara "fet" än att man skriver allt på en enda rad.
Kommentarer används bara när namnen på variablerna, funktionerna, klasserna ej kan beskriva vad som händer.
Jag återanvänder alltid andras kod, dvs jag använder ofta bibliotek skriven av andra, för att minimera mitt kodskrivande. Desto mindre kod jag skriver med ändå löser problemet, desto bättre blir min kod.

Men gång på gång stöter jag på folk som har Interface och Factory exakt överallt i sin kod och jag begriper inte varför dom har det exakt överallt. Jag kan förstå att om man har en abstrakt klass som innehåller exakt allt, sedan vill man "filtrera" bort metoder och flät och skapa ett helt nytt objekt. Då passar interface bra. Men enligt mitt tycke så är arv ett riktigt dåligt sätt att programmera med, om det är inte absolut nödvändigt.

Ibland ser jag många använder Factory och jag begriper inte varför man ska ha det. Kunde man inte lösa detta på något annat sätt?

Ja, exakt. Utan att ange.

https://sv.wikipedia.org/wiki/Fabriksmetod

Är jag ensamen om detta, eller känner ni också att det är svårt att läsa andras koder? Vad kan det beror på? Dåliga programmerare, eller dålig kod?

Interfaces används för att skapa abstrakta kontrakt där användaren känner till minimalt med funktion.

Du kan ha ett interface som heter ICar, allt detta interface har är en metod som heter Drive().
Du har ett garage som pekar på en ICar.
Du går ut till ditt garage, det står en Volvo där, det vet inte du. Allt du gör är att gå till Volvon och säga Drive().
Nästa dag står det en Mercedes där, det vet inte du, du vet bara om Drive().

På samma sätt vill du ha alla dina implementationer så abstrakta som möjligt, så att dels personen som använder dina implementationer inte känner till hur det är implementerat. Sen dels för att man ska kunna byta ut en implementaton av något abstrakt kontrakt, utan att hela applikationen crashar.

Sen underlättar interfaces enormt för testning, då allt du behöver mocka är Drive().
Hade du istället behövt skicka in en konkret Volvo så är det väldigt mycket du behöver skapa upp och mocka bort för att kunna testa ditt garage. Då du egentligen skiter i vilken bil som står där. Du kanske bara vill testa att din port går att stänga.

Factory används för att centrera ett ställe för att skapa upp ett objekt. Det kan vara för att det krävs mycket logik eller andra beroenden att skapa upp detta objekt, men att man kanske behöver objektet på olika ställen i koden.
Då gör man en factory där man kan be om det objektet och få det.
Ofta kan detta då göras i samband med ett interface, att metoden ger tillbaka en ICar men beroende på någon inställning i config eller parameter på metoden kan du välja om du vill ha en Volvo eller en Mercedes.

Permalänk
Medlem

@heretic16
En annan sak att tillägga då jag inte kan redigera mitt inlägg. En klass kan implementera flera interfaces, men är låst att ärva från en klass. Det ger möjlighet att utöka funktionalitet där man behöver det och man låser sig inte till en klass.

Ett simpelt exempel skulle vara att ha två olika interfaces för Read och Write funktioner. Sen har man en klass implementera båda. Sen kan man på övriga ställen sätta beroenden till IRead eller IWrite beroende på vad man behöver komma åt för metoder.
Då kan man säkerställa att en viss klass endast har tillgång till funktioner för att läsa.

Sen kan du via interfaces utöka klasser med extensions, likt hur .NET och C# gör med IEnumerable, IList osv.
Låt oss säga att du har en sorteringsfunktion som bygger vidare på ett interface med en eller två egenskaper som används för att sorteringen ska bli korrekt. Då kan du säga att din klass implementerat det interfacet så får du tillgång till extensionmetoden.

En annan fördel med interfaces är att du kan swappa implementationer väldigt enkelt. Låt oss säga att du implementerat en ny feature, istället för att ändra befintliga klassen kan du implementera en ny klass av samma interface. Sedan via DI-containern kan du toggla mellan vilken av dessa implementationer som ska köras, t.ex. via din config. Då kan du enkelt parallellköra dessa för att testa att allt fungera som det ska. Skulle något gå sönder kan du enkelt gå tillbaka till tidigare implementation.

Permalänk
Medlem

Jag ska bygga en grej som kopplar med bluetooth på jobbet, eftersom produkter från olika märken beter sig lite olika även om de har samma ändamål så tänker jag bygga ett interface mellan min kod och aparaterna, kanske kalla den Connectable.

Då behöver jag inte koppla min egen kod till specifika implementationer utan jag kan bara skriva en kodsnutt på sidan som tar reda på vilket märke prylen är, gör en Connectable av den och min kod är glad.

Permalänk
Medlem

Många bra förklaringar redan. Det bästa exemplet på interfaces i Java är nog List. Om du definerar en List kan du sedan sätta den till en klass som fullföljer kontraktet, t ex en ArrayList. Sen kanske du behöver en LinkedList istället och då även den fullföljer samma kontrakt räcker det att du ändrar vid tilldelningen på List. På samma sätt kan du definera en egen list klass och tilldela den om du vill.

Angående factories är det sällan jag skriver några men ibland använder man de omedvetet när man använder diverse bibliotek. Används främst när man bygger komplexa objekt. Finns bra exempel här: https://stackoverflow.com/questions/69849/factory-pattern-whe...

Permalänk
Avstängd

Ett exempel som jag jobbat med är en mjukvara som hittar bästa vägen genom en graf och reserverar alla noderna som behövs på vägen, i tid likväl som position. Då har vi en ReservationFactory som returnerar en IReservation som kan vara en av många typer beroende på vart i grafen man befinner sig just nu och förstås en massa andra saker. Till exempel punkt- eller segmentreservation men det kan ju också bli en väntereservation om nästa nod är upptagen i tiden bland annat.