[C#, .Net] Vad är poängen/syftet med Interfaces?

Permalänk

[C#, .Net] Vad är poängen/syftet med Interfaces?

Jag programmerar inom C#/.Net och försöker förstå mig på syftet med Interfaces. Jag vet att .Net och inte stödjer multilevel-inheritance och det problemet kan lösas med Interfaces. Om vi då kikar på följande modell:

public abstract class Person { string Name { get; set; } int WorkExperience { get; set; } decimal Salary { get; set; } DateTime Birthday { get; set; } bool IsFemale { get; set; } public virtual decimal Contract(bool hasBeenSigned, decimal money) { return (decimal)Math.PI * (money * 9.99m); } } //We implement IEngineerTask public class FullTimeEmployee : Person, IEngineerTask { double MaxWorkingHours { get; set; } List<Task> Tasks { get; set; } public int DoPhysics() { throw new NotImplementedException(); } public Delegate Resources(Dictionary<int, byte> computerResources, params string[] credentials) { throw new NotImplementedException(); } public float CalculateMass(string gConstant, float objectWeight) { throw new NotImplementedException(); } public Physics PhysicsDepartment { get; set; } } public class Task { int TaskId { get; set; } string TaskName { get; set; } } interface IEngineerTask { int DoPhysics(); Delegate Resources(Dictionary<int, byte> computerResources, params string[] credentials); float CalculateMass(string gConstant, float objectWeight); Physics PhysicsDepartment { get; set; } } public enum Physics { QuantuamMechanics, StringTheory, NewtonianGravity, GeneralRelativy, SpecialRelatvity }

Klassen ...

public class FullTimeEmployee { }

... kan ju utelämna interfacet IEngineerTask samtidigt som klassen ser likadan ut med precis samma class members:

//IEngineer is omited public class FullTimeEmployee : Person { double MaxWorkingHours { get; set; } List<Task> Tasks { get; set; } public int DoPhysics() { throw new NotImplementedException(); } public Delegate Resources(Dictionary<int, byte> computerResources, params string[] credentials) { throw new NotImplementedException(); } public float CalculateMass(string gConstant, float objectWeight) { throw new NotImplementedException(); } public Physics PhysicsDepartment { get; set; } }

Detta väcker liksom funderingar och frågetecken varför Interfaces används Nån som kan klargöra konceptet med det? Har liksom IEngineerTask en mer "beskrivande" syfte som t.ex. särskiljer en FullTimeEmployee som implementerar t.ex. en ISalesTask?

Permalänk
Medlem
Skrivet av DiAMONDBACK:

Jag programmerar inom C#/.Net och försöker förstå mig på syftet med Interfaces. Jag vet att .Net och inte stödjer multilevel-inheritance och det problemet kan lösas med Interfaces. Om vi då kikar på följande modell:

public abstract class Person { string Name { get; set; } int WorkExperience { get; set; } decimal Salary { get; set; } DateTime Birthday { get; set; } bool IsFemale { get; set; } public virtual decimal Contract(bool hasBeenSigned, decimal money) { return (decimal)Math.PI * (money * 9.99m); } } //We implement IEngineerTask public class FullTimeEmployee : Person, IEngineerTask { double MaxWorkingHours { get; set; } List<Task> Tasks { get; set; } public int DoPhysics() { throw new NotImplementedException(); } public Delegate Resources(Dictionary<int, byte> computerResources, params string[] credentials) { throw new NotImplementedException(); } public float CalculateMass(string gConstant, float objectWeight) { throw new NotImplementedException(); } public Physics PhysicsDepartment { get; set; } } public class Task { int TaskId { get; set; } string TaskName { get; set; } } interface IEngineerTask { int DoPhysics(); Delegate Resources(Dictionary<int, byte> computerResources, params string[] credentials); float CalculateMass(string gConstant, float objectWeight); Physics PhysicsDepartment { get; set; } } public enum Physics { QuantuamMechanics, StringTheory, NewtonianGravity, GeneralRelativy, SpecialRelatvity }

Klassen ...

public class FullTimeEmployee { }

... kan ju utelämna interfacet IEngineerTask samtidigt som klassen ser likadan ut med precis samma class members:

//IEngineer is omited public class FullTimeEmployee : Person { double MaxWorkingHours { get; set; } List<Task> Tasks { get; set; } public int DoPhysics() { throw new NotImplementedException(); } public Delegate Resources(Dictionary<int, byte> computerResources, params string[] credentials) { throw new NotImplementedException(); } public float CalculateMass(string gConstant, float objectWeight) { throw new NotImplementedException(); } public Physics PhysicsDepartment { get; set; } }

Detta väcker liksom funderingar och frågetecken varför Interfaces används Nån som kan klargöra konceptet med det? Har liksom IEngineerTask en mer "beskrivande" syfte som t.ex. särskiljer en FullTimeEmployee som implementerar t.ex. en ISalesTask?

En viktig poäng är att när du har interfacet så kan du ha flera helt olika klasser som implementerar detta och använda metoderna definierade i interfacet utan att den anropande koden ska behöva veta vilken implementation det handlar om.

Du kan då någon annanstans t.ex. ha en metod som tar en IEngineerTask som argument och använder sig av metoderna definierade där i. Du kan både passa in din FullTimeEmployee såväl som andra implementationer av IEngineerTask.

Om interfacet inte finns så skulle samma metod behöva ta en FullTimeEmployee som argument istället och den blir då låst till att bara kunna hantera just denna klass (och subklasser).

(Dock upplever jag IEngineerTask-exemplet som något underligt.)

Visa signatur

Desktop: Ryzen 5800X3D || MSI X570S Edge Max Wifi || Sapphire Pulse RX 7900 XTX || Gskill Trident Z 3600 64GB || Kingston KC3000 2TB || Samsung 970 EVO Plus 2TB || Samsung 960 Pro 1TB || Fractal Torrent || Asus PG42UQ 4K OLED
Proxmox server: Ryzen 5900X || Asrock Rack X570D4I-2T || Kingston 64GB ECC || WD Red SN700 1TB || Blandning av WD Red / Seagate Ironwolf för lagring || Fractal Node 304

Permalänk
Skrivet av evil penguin:

En viktig poäng är att när du har interfacet så kan du ha flera helt olika klasser som implementerar detta och använda metoderna definierade i interfacet utan att den anropande koden ska behöva veta vilken implementation det handlar om.

Du kan då någon annanstans t.ex. ha en metod som tar en IEngineerTask som argument och använder sig av metoderna definierade där i. Du kan både passa in din FullTimeEmployee såväl som andra implementationer av IEngineerTask.

Om interfacet inte finns så skulle samma metod behöva ta en FullTimeEmployee som argument istället och den blir då låst till att bara kunna hantera just denna klass (och subklasser).

(Dock upplever jag IEngineerTask-exemplet som något underligt.)

Tack för replik!

Men jag måste erkänna att det fortfarande är lite luddigt... ska fundera på det du skrev.
Och ja, IEngineerTask--grejen är nog inte världens bästa utgångspunkt

Permalänk
Hedersmedlem
Skrivet av DiAMONDBACK:

Men jag måste erkänna att det fortfarande är lite luddigt... ska fundera på det du skrev.

Om man accepterar att det kan vara praktiskt att ärva från en Person-klass från början är ju inte steget så långt till att vilja kunna gruppera sina objekt även på andra sätt. Kanske vill man ha en lista med objekt som man kan köra DoPhysics() på? Genom att lyfta ut IEngineerTask-egenskaperna kan man hantera sådana objekt utan att tänka på hur de ser ut i övrigt.

Permalänk
Medlem

Det klassiska Pizza exemplet är mer lätt förstått...

http://stackoverflow.com/questions/6802573/c-sharp-interfaces...

Poängen är att koden ska bli mera modulär, det är väl på gott och ont.

Alla pizzor har massa gemansamma moment, pizzadegen ska bakas ut, ost, tomat lägg den i en kartong... m.m. som gäller alla pizzor.

En pepperoni pizza har korv på sig och Hawaiin har annans och skinka, lite grejjer skiljer sig åt..

Istället för att göra Separata metoder till både Pepperoni pizza och Hawaii pizza, kan du implementera interfacet IPizza , och påverka din grund pizza metod genom koden där. En form av polyforism ...

Enda haken är att alla variabler och metoder som är deklarerat i Interfacet måste du skriva egna implementationer för.

Sen kan du med fördel använda dig av tex abstract factory deign mönstret ...

Visa signatur

// Lian Li PC-888 // i7 4790s // MSI vega 56 8gb O.C. // 16GB //

Permalänk
Medlem

Läs på om SOLID.

http://en.wikipedia.org/wiki/SOLID_%28object-oriented_design%...

Bra programmerare följer solid och det kostar ingen tid men sparar tid och gör kod lättare att läsa och underhålla. Dåliga programmerare förstår sig inte på solid och skriver ofta spagettikod som är kostsam att vidareutveckla, ändra och underhålla.

Visa signatur

Intel Core i7 8700K, MSI GeForce GTX 1080 Ti 11GB Gaming X, Samsung 960 EVO 1TB, MSI Z370 GAMING M5, Corsair 32GB (4x8GB) DDR4 3200MHz CL16 Vengeance, EVGA Supernova G3 850W

INTEL CORE I7 3930K 3.20GHZ 12MB S-2011, FRACTAL DESIGN MIDITOWER DEFINE R3, CORSAIR HX 1050W, ASUS RAMPAGE IV FORMULA, Asus STRIX GTX970, CORSAIR 16GB DDR3 DOMINATOR QUAD 1866MHZ CL9 (4X4GB) Ljud: ASUS Xonar D2X/XDT 7.1 | Elac 5.1 +förstärkare | Cambridge dacmagic plus | Astro gaming A40 | Sennheiser HD 650
You ask me if I have a god complex? Let me tell you something, I am god!

Permalänk

Tack för er tid! Saker & ting är tydligare

Permalänk
Medlem

Ett exempel jag personligen tycker är ganska enkelt att förstå är "tankbara" och "sittbara" objekt.

En bil är både sittbar och tankbar.
En cykel är sittbar, men ej tankbar.
En motorsåg är tankbar, men ej sittbar.

Vi skapar då interfaces för sittbar och tankbar:

interface IFuelable { void Fuel(int amount); } interface ISeatable { void SeatPerson(string name); }

Vi kan sedan implementera dessa interfaces i klasserna Car, Bicycle, och Motorsaw:

class Car : ISeatable, IFuelable { public void SeatPerson(string name) { Console.WriteLine("{0} sat down in a car.", name); } public void Fuel(int amount) { Console.WriteLine("A car was fueled with {0} litres of gas.", amount); } } class Bicycle : ISeatable { public void SeatPerson(string name) { Console.WriteLine("{0} sat down on a bike.", name); } } class Motorsaw : IFuelable { public void Fuel(int amount) { Console.WriteLine("A motorsaw was fueled with {0} litres of gas.", amount); } }

Eftersom Car implementerar både IFuelable och ISeatable, så kan den refereras genom båda typerna

List<IFuelable> gasPumpQueue = new List<IFuelable>(); gasPumpQueue.Add(new Car()); gasPumpQueue.Add(new Motorsaw()); foreach (IFuelable f in gasPumpQueue) { f.Fuel(2); } List<ISeatable> parkingLot = new List<ISeatable>(); parkingLot.Add(new Car()); parkingLot.Add(new Bicycle()); foreach (ISeatable s in parkingLot) { s.SeatPerson("Olle"); }

Permalänk

Om Interfaces
----------------
Jag skrev ett system en gång. Där hade jag en klass A som gjorde något. Och senare skapade jag en till klass B som var helt annorlunda och den gjorde nåt helt annat. Senare skapade jag ett GUI och ville visa båda klasserna i GUIt. Då ville jag ha funktioner som tog emot en A eller en B klass och gjorde saker med dem. Men det blev struligt eftersom A och B inte hade en gemensam basklass. Ifall de hade en gemensam basklass så skulle jag kunna skapa en pekare till basklassen och då kan min funktion ta emot A eller B objekt. Men nu gick det inte eftersom A ärvde från Person, och B ärvde från TimeScheduler.

Så subklassen A ärver från klassen Person. Och subklassen B ärver från klassen TimeScheduler.

int DrawThis (Person *itemToDraw) {...}

Eller borde funktionen se ut så här kanske istället?
int DrawThis (TimeScheduler *itemToDraw) {...}

Mitt problem är alltså att jag vill hantera både objekt av typen A och B. Men A och B har inget gemensamt, de har helt olika basklasser.

Jag kan lösa detta genom att skapa en ny superklass till Person och TimeScheduler, kalla den PersonAndTimeScheduler. Och då kan min funktion se ut så här:
int DrawThis (PersonAndTimeScheduler *itemToDraw) {...}
Och nu kan jag stoppa in både A eller B objekt. I detta fall är PersonAndTimeScheduler superklass till både Person och TimeScheduler. Och A ärver från Person, och B ärver från TimeScheduler.

Men detta är meckigt, att ändra i klasshierarkin, det är ju jobbigt att skap nya superklasser. Hur ska vi lösa detta? Vi har ju inte multipelt arv i Java. Jo, lösningen är Interfaces. Så här funkar det: Både Person och TimeScheduler implementerar ett Interface som heter IPersonAndTimeScheduler, denna ändring är lätt att göra och kräver bara en rad kod. Och då kan jag skriva så här
int DrawThis (IPersonAndTimeScheduler *itemToDraw) {...}
Så på nåt sätt, så kopplade jag ihop Person och TimeScheduler mha ett Interface trots att de inte har något gemensamt alls.

Läs detta igen och fråga på.