Jag har börjat läsa på lite om C# men fattar inte riktigt en sak angående Interface. Måhända att det förklaras senare i boken jag läser, men det verkar inte så: Vitsen med interface är om du har två eller fler klasser som implementerar samma interface. Dessa klasser "ser likadana ut" och kan användas som om det vore samma klass. Ingen bra förklaring, men jag kan inte komma på något tydligare sätt att förklara det på, hoppas att det iaf gav något! :) Ett exempel: Nä, att det var helt onödigt trodde jag inte att det var- då hade ju inte Interface funnits överhuvudtaget. Jag har som sagt bara inte fått det ordentligt förklarat för mig... Jag skulle försöka förklara det genom att säga att syftet med att använda interface är att man ska kunna återanvända sådan kod som använder sig av interfacen, eftersom den koden då inte är beroende av konkreta klasser. Jo, att bemästra polymorfism/interface har jag förstått att det är kärnan i OOP. Jag fick det inte att hänga ihop bara av det jag läste i boken - de sa helt enkelt att "Interfaces is a key factor in object oriented programming..." bla bla bla, men de kunde inte bistå med några bra exempel på varför och hur det skulle implementeras. För att vara en så viktig del i C# och OOP så tycker jag minsann att de kunde använt mer än 3-4 sidor för att förklara detta... Ja, det där med dåliga exempel på polymofism låter bekant, Tack för ett oerhört uttömmande svar!! Det där ger mig mycket att spinna vidare på när jag hugger in på nästa bok... och nästa gång jag exprimenterar vid datorn. Kenneth, Jag glömde förresten att nämna en intressant situation då polymorfism via interface är väldigt användbart, nämligen då man hamnar i en situation då kan man kanske egentligen skulle nöja sig med att ärva och överrida en metod men är förhindrad att göra detta p.g.a. att klassen som man vill ärva är sealed.En konceptuell fråga ang Interface
VARFÖR i hela friden skall jag använda mig av Interface? Även om jag deklarerar ett interface med alla parametrar, metoder och dylikt så måste jag ju ÄNDÅ skriva samma saker i någon klass som utför det jag vill göra med metoderna osv... Känns som overkill att behöva skriva en definition som ändå kan utläsas i klassen.
Javisst - jag kan ärva ett eller flera interface, men likväl... Jag får inte den här dubbleringen av information att gå ihop. Kan någon förklara det för mig?
Tack!Sv: En konceptuell fråga ang Interface
/JohanSv: En konceptuell fråga ang Interface
Säg att du har en ArrayList. Den fylls med information ifrån en xml-fil. Helt plötsligt så byts datakällan ut mot en databas och arraylisten fylls via en extern komponent.
Hela din kod byggde förut på att det var en arraylist som retunerades. Helt plötsligt så får du problem vid bytet av datakällan då den externa komponenten retunerar en annan lista.
Nu är det så att både ArrayList och den lista som komponenten retunerade ärvde interfacet IList.
Om du istället för att ha förlitat dig på att det var klassen ArrayList hade använt interfacet IList så hade inte koden behövts ändrats någonting!
Ytterligare ett exempel är att du skriver på t.ex. ett pluginsystem. Du laddar där pluginen helt dynamiskt. Nu är det dock så att en del plugins kan behöva tillgång till "unmanaged" kod, dvs. kod som inte clr hanterar. Det kan vara t.ex. fönster, filer etc.
Nu är det dock så att alla plugins inte behöver stödet för upprensning.
Om alla plugins som behöver det stödet såg till att ärva interfacet IDisposable så kunde man enkelt när man körde koden kolla om klassen hade det interfacet. Om så var fallet kunde man enkelt stoppa in objektet i en IDisposable-variabel och sedan bara anropa metoden .Dispose()
Ytterligare ett exempel är att du vill bygga en enkel platform för att kunna lägga till funktionallitet till ditt program. För att inte begränsa vilken basklass man måste använda på tilläggen så kan de istället implementera olika interface beroende på vilken funktionallitet de måste ha.
Till sist: det är inte onödigt :)Sv:En konceptuell fråga ang Interface
Men jag undrar om det inte stilla börjar gå upp ett ljus för mig...
Tack för svaren; Jag kanske får anledning att återkomma när jag läst klart boken och exprimenterat lite.Sv: En konceptuell fråga ang Interface
De konkreta klasserna skickar man in som parametrar till metoderna, medan de är deklarerade/typade som ett interface som den konkreta klassen implementerar.
Implementationen av en sådan metod är således endast beroende av interfacen i signaturen och inte den konkreta klassen.
Implementationen av en metod kan också välja konkret klass som ska returneras, medan den returnerade typen är deklarerad som ett interface i metodens signatur.
I detta fall är det den anropande koden som inte är beroende av någon konkret klassen utan är endast beroende av returtypens interface i metodens signatur.
(det sistnämnda är en s.k. factory method, d.vs. ett GoF-designmönster)
Här är två TUMREGLER för att återanvända kod med hjälp av interface:
(1) Datatyperna (returvärden och in-parametrar) i en metods signatur bör vara deklarerade som interface hellre än klasser.
(2) Datayperna för attributen/fälten/medlemsvarablerna/egenskaperna i en klass bör vara deklarerade som interface hellre än klasser.
Själva konceptet kallas polymorfism eller dynamisk bindning (vilket även kan erhållas genom att använda basklasser istället för interface, men det är bättre att använda interface, inte minst för att man då slipper förbruka det enda arv man har i språk såsom C# eller java)
Det är mycket viktigt att bemästra polymorfism/interface om man verkligen vill kunna programmera objektorienterat och återanvända kod på ett bra sätt.
Väldigt många av de elementära designmönstren (GoF-mönster) är baserade på polymofism.
/ TomasSv:En konceptuell fråga ang Interface
Sv: En konceptuell fråga ang Interface
även om jag inte har läst just den boken.
När jag en gång i tiden började med att försöka lära mig
objektorienterad programmering (C++) så kommer jag ihåg
att jag också tyckte att polymorfism inte verkade särskilt
meningsfullt, men det berodde på att exemplen jag fick se inte
var tillräckligt övertygande. De var lite för triviala av denna
typ (nu använder jag dock c# syntax nedan):
MittInterface o = new MinKlass1();
o.metod();
Visserligen fungerar såna exempel bra för att lära sig syntaxen,
men problemet är att det inte är särskilt meningsfullt att
använda interface inom en metod där man ändå alltid
instansierar med samma klass.
Jag hade själv önskat mig exempel som bättre skulle ha illustrerat nyttan
med polymorfism, t.ex. så här (men med bättre metod/klassnamn förstås :-)
EnAnnanKlass enAnnanKlass = new EnAnnanKlass();
enAnnanKlass.enAnnanMetod(new MinKlass1());
enAnnanKlass.enAnnanMetod(new MinKlass2());
public class EnAnnanKlass
{
// metod som kan återanvändas för olika klasser m.h.a.
// polymorfistiskt anrop på instansen som skickas in:
public void enAnnanMetod(MittInterface mittInterface)
{
// kod.....
mittInterface.metod();
// kod.....
}
}
public interface MittInterface
{
void metod();
}
public class MinKlass1: MittInterface
{
public void metod()
{
// ....
}
}
Det väsentliga felet med de exempel jag en gång fick lära
mig var alltså att man bara använde interfacen inom samma
metoder som instanseringen gjordes.
Det är oftast onödigt eftersom man då lika gärna kan anropa
metoderna direkt på klassen istället.
Visserligen är det _möjligt_ att utnyttja meningsfull polymorfism
inom en metod genom att göra t.ex. så här:
public class X {
public void metodX() {
// kod....
MittInterface o;
if(villkor) {
o = new MinKlass1();
}
else {
o = new MinKlass2();
}
o.metod();
// kod....
}
// ... deklarera variabeln "villkor" etc. ..
}
Men jag skulle inte göra på detta viset utan skulle istället
lägga instansieringen i en s.k. factory method som då returnerar
en instans av en klass som implementerar det interface
som signaturens returtyp definierar, d.v.s. så här:
public class X {
public void metodX() {
// kod....
MittInterface o = getImplementation();
o.metod();
// kod....
}
protected MittInterface getImplementation() {
if(villkor) {
return new MinKlass1();
}
else {
return new MinKlass2();
}
}
// ... deklarera variabeln "villkor" etc. ..
}
När det gäller designmönster, så kan man nu
betrakta ovanstående metodX som en "Template method", som anropar
factory-metoden "getImplementation" som då inte bara är
en "factory method" utan även fungerar
som en hook-metod (eller "primitive operation" som den
också brukar kallas i designmönstret Template method).
En fördel med att bryta ut instansieringen till en egen factory-metod
är alltså att man då kan överrida/omdefiniera den i en
subklass samtidigt som man återanvänder resten av koden i
template-metoden.
För övrigt kan jag även förtydliga det jag nämnde om tumreglerna
i mitt förra inlägg, d.v.s. att meningsfull användning av interface bygger på att man ska
använda interface i signaturer, samt att deklarera medlemsvariabler som interface
(se t.ex. designmönstet "Strategy pattern" för ett exempel på det sistnämnda).
Jag menade t.ex. inte att man nödvändigtvis hela tiden ska använda interface
i alla metoders signaturer, utan snarare att det är en grundläggande bra
utgångspunkt som man bör ha i åtanke (ungefär i analogi med att det är en fin teoretisk
tanke att sträva efter en normaliserad databas utan duplicerad data, men att
det i praktiken ändå kan vara önskvärt med duplicerad data för att
få bättre prestanda i selekteringar).
Om man är pragmatisk (t.ex. anhängare av XP), så vill man nämligen gärna undvika
att slösa tid på att skapa interface i onödan, bara för att
man _kanske_ nån gång i framtiden kommer att vilja implementera en
klass på mer än ett sätt utan istället kör man då _vid behov_ en
s.k. refactoring som bryter ut de metoderna som man vill kunna använda
på ett sätt som är gemensamt med en annan klass.
http://www.refactoring.com/catalog/extractInterface.html
Om man använder Eclipse (Java) så är detta oerhört enkelt att göra,
och för C# -användare så kommer en sådan refactoring-funktion ("Extract Interface")
att finnas med i Visual C# 2005 enligt:
http://msdn.microsoft.com/vstudio/productinfo/roadmap.aspx
Sedan kan jag också förtydliga det jag skrev om mina tumregler med
att säga att det inte räcker med att "deklarera" datatyperna
i signaturerna som interface, för om man sedan måste downcasta för
att kunna anropa vissa metoder så försvinner en del av nyttan...
Detta kan tyckas självklart, men jag har själv kommit i kontakt
med smått obegriplig kod med obefintlig semantik i metoderna
eftersom de tar emot ett "Object" som sedan har implementerats med diverse
onödigt krånglig kod som använder reflektion och downcasting för att slutligen
anropa en metod till någon subklass till Object (som alla objekt ärver).
Motiveringen från programmeraren, var att det skulle vara "generellt" att använda
en generell datatyp, och det kanske han tyckte när han implementerade koden,
men ur underhållssyfte för den stackare som måste försöka förstå koden
är det definitivt ingen höjdare med kod som baseras på att downcasta Object-typer.
Generella datatyper (interface eller basklasser) är alltså bra att använda
för samma typer av objekt som verkligen innehåller samma metoder utan
att behöva downcasta, men om man tycker sig ha ett behov att downcasta
ett interface (eller basklass) så bör man gärna försöka tänka till en gång extra för
att förhoppningsvis kunna förbättra sin design så att man slipper downcasting.
/ TomasSv:En konceptuell fråga ang Interface
Sv: En konceptuell fråga ang Interface
Det lättaste att tänka med tnterface är att de klasser som implementerar ett visst interface lovar att uppfylla den funktionaliteten som interfacet deklarerar. Det gör att en klass kan ha många ansikten, och att kod inte då behöver bry sig om själva typen av ett objekt, bara att det uppfyller den funktionalitet som behövs. Enkelt sagt är ett interface ett kontrakt som säger vilken funktionalitet en klass skall uppfylla - är kontraktet underskrivet skall det efterlevas annars får man på fan =)
<b>PS.</b>Ta gärna för vana att markera dina inlägg som <b>Löst</b> om du fått ett svar som du är nöjd med, eller som <b>Stäng</b> om du inte fått ett svar som du är nöjd med/löste ditt problem men du övervakar inte aktivt tråden mer för fler svar. På så sätt kan andra besökare på Pellesoft enklare hitta lösningen på liknande problem då de kan se att det finns ett inlägg som innehållet ett svar.
Har du missat att göra detta på tidigare inlägg kan du enkelt hitta tillbaka till dem genom att gå till framsidan av forumet och titta längst upp, där finner du dina inlägg som inte är merkerade <b>Löst</b> eller <b>Stängd</b>.Sv: En konceptuell fråga ang Interface
Om man då har tur så implementerar klassen som man skulle vilja ärva ett interface som innehåller de metoder som man vill anropa, och i så fall kan man tillämpa designmönstret som heter Decorator som i likhet med väldigt många designmönster är baserade på polymorfism.
För den som är intresserad av ett exempel så kan ni läsa ett inlägg jag nyss gjorde i en annan diskussionstråd på följande URL:
http://www.pellesoft.se/communicate/forum/view.aspx?msgid=179412&forumid=10&sum=0#179558
Ifall URL-strukturen på pellesoft skulle ha förändrats när detta läses så kan jag nämna att den aktuella tråden är:
"Arv? Förklara det och visa gärna nått exempel kort"
och det aktuella inlägget är:
"Sv: Arv? Förklara det och visa gärna nått exe 2005-04-21 14:28:17 - Tomas Johansson"
och ligger i forumet "asp.net 1.x" (kanske fel forum, men det är i alla fall där det ligger f.n.)
/ Tomas