Interfejsy - co to, po co i jak uzywać

0

Co to sa interfejsy i po co tego uzywac w C#. Przeczytalem juz o tym w jednej ksiazce ale nic mi sie nie rozjasniło.

0

najprościej to interfejs to taka klasa ale baz ciał metod. Klasa w C# może dziedziczyć po tylko jednej klasie ale może implementować wiele interfejsów i tutaj jest największe pole do działania dla intefejsów

0

Przydaje się do tworzenia komponentów obsługujących różne rodzaje obiektów.

Np ostatnio w Javowym frameworku Apache Wicket (trochę wzorowany na Swingu) tworzę komponent do obsługi list. Komponent współdziała z podkomponentami (typu przycisk do swapowania sąsiednich elementów w liście). Całość wymaga tylko aby elementy listy implementowały stworzony przeze mnie interfejs IOrderableListItem (nazwa trochę kiepska, ale dopiero zaczynam zabawę z interfejsami) deklarujący metody setPosition i getPosition.

Dzięki temu dowolną listę w ORM-ie mogę użyć jako modelu do tego komponentu, wystarczy że dopiszę do deklaracji klasy "implements IOrderableListItem" i dodam pole position wraz z akcesorami.

Generalnie Misiekd opisał całą filozofię interfejsów. Wielodziedziczenie znane z C++ powoduje wiele niebezpieczeństw, trochę informacji jest np w http://pl.wikipedia.org/wiki/Dziedziczenie_(programowanie) .

0

ja też nie za bardzo czułem idee interfejsów, aż pewnego dnia mnie oświeciło (albo tak mi się tylko wydaje :-D )

Generalnie Interfejsów używa się (tworzy się własne interfejsy) na etapie 'modelowania' projektu... to znaczy za ich pomocą można sobie łatwo ustalić, jak powinny wyglądać obiekty(a właściwie metody), którymi powinniśmy się posługiwać w kodzie pisząc konkretny program. Łatwiej się później wszystko ogarnia, jak tworzy się już konkretne klasy obiektów dziedziczące z naszych interfejsów. Nie trzeba pamiętać jakie
metody mają zawierać te nasze klasy, bo to zostanie wymuszone przez interfejs. Ba, jednym kliknięciem (chyba w VS) zostanie wymuszona nawet implementacja metod, które wynikających z dziedziczonych interfejsów. Nam pozostanie wklepanie kodu w tych procedurach...

0

Oprocz tego mozesz sobie uogolnic dzialanie jakiejs metody albo okreslic czego Twoja metoda wymaga do dzialania. Np. piszesz metode robiaca select * na bazie:

public DataTable ReadTable(IDbConnection connection, String tableName)
{...}

W ten sposob stwierdzasz, ze nie interesuje Cie czy to polaczenie do SqlServera, Oracle'a, Mysqla, czy czegos innego. Interesuje, by obiekt przekazany jako pierwszy parametr umial zrobic Open(), Close() i CreateCommand(). To ostatnie zwraca np. IDbCommand, ktore ma wlasciwosc CommandText i ExecuteCommand(). W ten sposob odlaczasz sie od implementacji. Innymi slowy gwarantujesz wykonujacemu Twoja metode, ze zadziala poprawnie dla KAZDEJ bazy danych pod warunkiem poprawnej implementacji podanych metod.

W szczegolnosci obiekt typu IDbConnection moze byc implementacja, ktora nic nie robi, dzieki ktoremu mozesz sobie przetestowac czy Twoja metoda poprawnie sie zachowuje, czyli np. sprawdzic czy wywolujesz ExecuteCommand tylko raz (ze wzgledow wydajnosciowych), czy wykonujesz Open() - wtedy bedzie to mock, obiekt udajacy prawdziwy w celach testowych. Czyli ulatwiasz sobie unit testy, bo nie potrzebujesz do nich zywej bazy stojacej gdzies na serwerze, ktory moze przestac dzialac i rozwalic testy.

1

Witam

Podłączę się do tematu, również poznaję język C#, zdobyłem ogólną wiedzę o nim czytając materiały dostępne w internecie. Teraz pogłębiam i rozszerzam ją lekturą "Język C# 2010 i platforma .NET 4.0", doszedłem do interfejsów, od nowa przejrzałem materiały w sieci i dalej nie rozumiem ich zasady. Po co deklarować metody w interfejsie bez ciała jak w późniejszym czasie należy je dopisać w klasie implementującej interfejs. Nie rozumiem tu do daje w tym momencie interfejs. Przecież i tak metody muszą być zawarte w klasie.

Proszę o jasne wytłumaczenie tego zagadnienia, staram się go zrozumieć samemu ale nie potrafię ....

0
marcin82w napisał(a):

Po co deklarować metody w interfejsie bez ciała jak w późniejszym czasie należy je dopisać w klasie implementującej interfejs. Nie rozumiem tu do daje w tym momencie interfejs. Przecież i tak metody muszą być zawarte w klasie.

Klas implementujących dany interfejs możesz mieć bardzo wiele. Jeśli np. masz jakąś metodę, która przyjmuje argumenty danego interfejsu i operuje na nich, nie będzie miało później znaczenia, której konkretnie klasy obiekt przekażesz, ważne, żeby implementowała ten interfejs.

0

OK, ale przecież tworząc klasy implementujące interfejs muszę stworzyć dwie takie same metody w dwóch różnych klasach, więc muszę określić taki sam typ argumentów jakie te metody przyjmują. Interfejs tylko mnie do tego zobowiązuje, ale bez interfejsu dwie identyczne metody w różnych klasach posiadające takie same typy argumentów mogą przecież przyjmować te same dane czy obiekty i wykonywać na nich operacje.

Załóżmy że w klasach są dwie identyczne metody o treści

class kl_A
{
public int JakasMetoda (int[] x)
{ ciało metody }
}

class kl_B
{
public int JakasMetoda (int[] x)
{ ciało metody }
}

kl_A poz1 = new kl_A();
kl_B poz2 = new kl_B();

int x = poz1.JakasMeoda(argument);
int y = poz2.JakasMeoda(argument);

Przy wywołaniu tej metody podaję jako argument tablicę elem. int, i zarówno czy wywołam ją poz1.JakasMeoda(argument) czy poz2.JakasMeoda(argument) nie będzie to miało znaczenia. Chcąc czy nie obie klasy muszą mieć tą metodę. Nie bardzo rozumiem jakie znaczenie mają interfejsy. Co w tym przypadku da mi interfejs który będzie miał metodę JakasMetoda i kl_A i kl_B będą go miały.

To jast prosty przykład wymyślony z lotu, mogę tu zaimplementować interfejs ale nie muszę, w obu przypadkach będzie działało. W bardziej rozbudowanych przypadkach przecież można zrobić podobnie.

0

No to teraz pomyśl, że będziesz musiał stworzyć metodę w jakiejś innej klasie, która na wejściu chce dostać kl_A albo kl_B.
Nie rozumiesz problemu, bo patrzysz z punktu widzenia klasy, która go implementuje. Oczywiście, że ona sama zna swoje własne zachowanie. Interfejs jest pokazuje innym klasom, jak z daną klasą współpracować.

0
marcin82w napisał(a):

Interfejs tylko mnie do tego zobowiązuje, ale bez interfejsu dwie identyczne metody w różnych klasach posiadające takie same typy argumentów mogą przecież przyjmować te same dane czy obiekty i wykonywać na nich operacje.

To teraz nie używając interfejsu umieść obiekty tych różnych klas w jednej tablicy, a potem przejdź po niej pętlą i wywołaj te identyczne metody.

0

Dalej nie widzę problemu. Chcąc przekazać jakiejś metodzie z innej klasy obiekt kl_A muszę ją wcześniej odpowiednio zaimplementować. Zakładam że ta metoda będzie przyjmowała jako argument obiekt kl_A, muszę stworzyć taką metodę która będzie jako argument przyjmowała obiekt kl_A.

Nie mogę stworzyć metody powiedzmy:

void metodaXX(int x)

a następnie wywołując z jednej klasy tą metodę podać jako argument liczbę całkowitą, a z drugiej jako argument podać obiektu powiedzmy kl_A.

Więc dalej nie umiem znaleźć sensu interfejsów.

0
marcin82w napisał(a):

Dalej nie widzę problemu. Chcąc przekazać jakiejś metodzie z innej klasy obiekt kl_A muszę ją wcześniej odpowiednio zaimplementować. Zakładam że ta metoda będzie przyjmowała jako argument obiekt kl_A, muszę stworzyć taką metodę która będzie jako argument przyjmowała obiekt kl_A.

Ale ja napisałem obiekt kl_A ALBO obiekt kl_B - w jednej metodzie. Jak to rozwiążesz?
Zresztą somekind dał jeszcze lepszy przykład.

0

Dobrym przykładem będzie tutaj pętla foreach. Jeśli chcesz jakąś klasę przekazać do tej pętli, klasa ta (obiekt tej klasy oczywiście) musi dziedziczyć po interfejsie IEnumerable. Wiadomo, że ta pętla przeglądając sobie w jakiś sposób klasę wywoła metody które właśnie w swojej klasie musisz zaimplementować, aby umożliwić poprawne przeglądanie obiektu(zwykle w tym wypadku będzie to jakaś kolekcja). Skąd interfejs może wiedzieć jak przeglądać klasę? to Ty musisz zaimplementować odpowiednie metody... które są zadeklarowane właśnie w interfejsie.
Interfejsy w żaden sposób nie przekładają się na kod maszynowy. To służy tylko i wyłącznie do ułatwienia posługiwania się kodem wysokiego poziomu.

// i mie ma tak że nazwy metod są niejednoznaczne. W razie konfliktu trzeba jawnie wskazać, która metoda z którego interfejsu implementuje <- o ile się nie mylę.

0

Wiem że dla Was jest to oczywiste, ale czy możecie mi pokazać na jakimś konkretnym przykładzie w kodzie ? Chciałbym przeanalizować krok po kroku i postarać się zrozumieć dlaczego tego przykładu nie mógłbym zrealizować bez interfejsu.

1

Przykład prosty i trochę bez sensu, ale wyraża ideę:

using System;

namespace ConsoleApplication8
{
    class Program
    {
        static void Main(string[] args)
        {
            var figury = new IFigura[] {
                new Koło(4), 
                new Kwadrat(3), 
                new Koło(6.66), 
                new Kwadrat(123), 
            };

            foreach (var figura in figury)
            {
                WyświetlFigurę(figura);
            }

            Console.ReadLine();
        }

        private static void WyświetlFigurę(IFigura figura)
        {
            Console.WriteLine("Figura: {0} ma pole: {1} i obwód: {2}", figura.GetType().Name, figura.Pole, figura.Obwód);
        }
    }

    internal interface IFigura
    {
        double Pole { get; }
        double Obwód { get; }
    }

    class Koło : IFigura
    {
        private readonly double promień;

        public Koło(double promień)
        {
            this.promień = promień;
        }

        public double Pole
        {
            get { return Math.PI * this.promień * this.promień; }
        }
        public double Obwód
        {
            get { return Math.PI * this.promień * 2; }
        }
    }

    class Kwadrat : IFigura
    {
        private readonly double bok;

        public Kwadrat(double bok)
        {
            this.bok = bok;
        }

        public double Pole
        {
            get { return this.bok * this.bok; }
        }
        public double Obwód
        {
            get { return 4 * this.bok; }
        }
    }
}

A teraz usuń interfejs IFigura z kodu i zrób to samo.

0

Bez interfejsu:

 
class kl_A
{
public int JakasMetoda (int[] x)
{ ciało metody }
}
 
class kl_B
{
public int JakasMetoda (int[] x)
{ ciało metody }
}

class kl_C
{
   public void Foo(kl_A x)
   {
       //...
      x.JakasMetoda();
      //...
   }
   public void Foo(kl_B x)
   {
       //...
      x.JakasMetoda();
      //...
   }
}

Z interfejsem (w którego skład wchodzi JakasMetoda):

 
class kl_A : Interfejs
{
public int JakasMetoda (int[] x)
{ ciało metody }
}
 
class kl_B : Interfejs
{
public int JakasMetoda (int[] x)
{ ciało metody }
}

class kl_C
{
   public void Foo(Interfejs x)
   {
       //...
      x.JakasMetoda();
      //...
   }
}

Tutaj masz 2 klasy zawierające JakasMetoda, a co jak będzie ich 10? Będziesz musiał stworzyć dla każdej osobną wersję metody Foo. Ponadto bez interfejsu musisz każdorazowo zmieć kl_C, kiedy chcesz dodać obsługę obiektu innej klasy również zawierającej JakasMetoda. Problem znika, kiedy użyjesz interfejsu.

0

Muszę jeszcze raz wszystkie moje materiały przeczytać i na ich podstawie przeanalizować wasze przykłady. Powoli zaczynam dostrzegać sens interfejsów.

0

Czy chodzi np. o coś takiego (Java):
Mam metodę mojaMetoda(Object ob), której należy podać dowolny obiekt. Metoda ta wywołuje metodę jakasMetoda() tego obiektu. Tworzę więc interfejs mojInterfejs zawierający jakasMetoda(), a następnie mojaMetoda(Object ob) zmienia się w mojaMetoda(mojInterfejs ob) i teraz do mojejMetody mogę wysłać każdy obiekt implementujący mojInterfejs.
Dobrze zrozumiałem?

0
ShookTea napisał(a):

Czy chodzi np. o coś takiego (Java):
Mam metodę mojaMetoda(Object ob), której należy podać dowolny obiekt. Metoda ta wywołuje metodę jakasMetoda() tego obiektu. Tworzę więc interfejs mojInterfejs zawierający jakasMetoda(), a następnie mojaMetoda(Object ob) zmienia się w mojaMetoda(mojInterfejs ob) i teraz do mojejMetody mogę wysłać każdy obiekt implementujący mojInterfejs.
Dobrze zrozumiałem?

Jeśli dobrze Cię rozumiem, to dobrze zrozumiałeś :)

Może podam tak dodatkowo przykład z życia. Musiałem kiedyś zbudować dynamiczną kontrolkę GridView w ASP.NET. Kontrolka (GV) w niektórych komórkach miała zwierać nie znaną ilość list rozwijanych (DropDownList) lub po prostu zwykłą informację tekstową. Najpierw napisałem dwie klasy:

  • KomorkaZkontrolka, która jak sam nazwa wskazuje, za jej pomocą budowałem komórki, które zawierały dowolne kontrolki (Button, DropDownList, itd...)
  • KomorkaTekstowa, prosta komórka, która zawierała po prostu tekst

Obie klasy zwracały mi taki sam obiekt (DataControlField)

Była jeszcze trzecia klasa: WlasnyGV, która zawierała metodę dodaj komórkę. Owszem mogłem od razu podać jako argument tej metody obiekt: DataControlField. Jednak ładniej wyglądało podawanie jako argumentów metody obiektów: KomorkaZkontrolka lub KomorkaTekstowa. Właśnie w tym momencie utworzyłem interfejs Ikolumna, po którym dziedziczyły obie klasy komórek. Interfejs oczywiście wymuszał metodę, która zwraca DataControlField. Dzięki temu w klasie WlasnyGV w metodzie dodawania mogłem użyć argumentu typu Ikolumna (interfejs) i przekazywać zarówno obiekty: KomorkaZkontrolka i KomorkaTekstowa.

Jest to bardzo ogólny przykład. Ogólnie da się to zrobić bez interfejsów, ale dzięki nim łatwiej się czyta kod :)

0

Jeszcze jedna wariacja zmieniająca nieco przykład Somekind:

 
namespace MagnetofonKasetowy
{
    
    enum TypKasety  { C60, C90}
    
    interface IObsluga_MK
    {
        TypKasety typKasety { get; set; }
        bool start();
        bool stop();
        bool nagrywaj();
    }

    class Kasprzak : IObsluga_MK
    {

        public TypKasety typKasety { get; set; }

        public bool start()
        {
            try
            {
                //Uruchamia start w Kasprzaku
                return true;
            }
            catch
            {
                return false;
            }
            
        }

        public bool stop()
        {
            //Uruchamia stop w Kasprzaku
            return true;
        }

        public bool nagrywaj()
        {
            //Nagrywa na Kasprzaku
            return true;
        }
    }

    class Grundig : IObsluga_MK
    {

        public TypKasety typKasety { get; set; }

        public bool start()
        {
            //Uruchamia start w Grundigu
            return true;
        }

        public bool stop()
        {
            //Uruchamia stop w Grundigu
            return true;
        }

        public bool nagrywaj()
        {
            //Nagrywa na Grundigu
            return true;
        }

        class obslugaMagnetofonow
        {
            private List<IObsluga_MK> zasobyMagnetofonowe()
            {
                List<IObsluga_MK> zasobyMagnetofonowe = new List<IObsluga_MK>();
                zasobyMagnetofonowe.Add(new Kasprzak());
                zasobyMagnetofonowe.Add(new Grundig());
                return zasobyMagnetofonowe;
            }

            public void nagrajZKopia()
            {
               //Dzięki zastosowaniu interface ta metoda nie ulegnie zmianie 
               //po dodaniu kolejnego magnetofonu (nawet nowego typu)
               //lub po zmianie jednego typu magnetofonu na inny
                List<IObsluga_MK> zasoby = zasobyMagnetofonowe();
                //Założenie kaset i nagrywanie
                foreach (var row in zasoby)
                {
                    row.typKasety = TypKasety.C60;
                    row.nagrywaj();
                }
            }
        }
    }
}

Pozdrawiam,
Zoritt

0

Interfejsy mówią o tym co dana klasa może robić. Jednym z przykładów użycia jest np. praca w zespole gdzie jeden gość projektuje jak dana klasa ma wyglądać a kilka innych osób implementuje zgodnie z tym szablonem. Nim większy projekt tym bardziej docenia się interfejsy. Przy małych projektach może wydawać Ci się, że są niepotrzebne ale gdy pojawi się klasa która jest w bardzo mocny sposób powiązana z inną klasą to możesz naprawdę docenić użycie interfejsów.

Pokaże Ci przykład użycia interfejsu który wymusza, że dokończysz implementacje kodu:

Załóżmy, że chcę zlecić Ci napisanie implementacji zachowania żołnierza to wysyłam Ci plik, którego nie zmieniasz tylko traktujesz jako szablon:

public interface ISoldier
{
  void Draw();
  void Die();
  void Shoot();
  void TurnLeft();
  //...
}

public partial class ComputerControlledSoldier : ISoldier {}
public partial class PlayerControlledSoldier : ISoldier {}
0

Jeszcze jeden przykład na magnetofonach:

 
namespace MagnetofonKasetowy
{

    enum TypKasety { C60, C90 }

    interface IObsluga_MK
    {
        TypKasety typKasety { get; set; }
        bool start();
        bool stop();
        bool nagrywaj();
    }

    class Kasprzak : IObsluga_MK
    {

        public TypKasety typKasety { get; set; }

        public bool start()
        {
            try
            {
                //Uruchamia start w Kasprzaku
                return true;
            }
            catch
            {
                return false;
            }

        }

        public bool stop()
        {
            //Uruchamia stop w Kasprzaku
            return true;
        }

        public bool nagrywaj()
        {
            //Nagrywa na Kasprzaku
            return true;
        }
    }

    class Grundig : IObsluga_MK
    {

        public TypKasety typKasety { get; set; }

        public bool start()
        {
            //Uruchamia start w Grundigu
            return true;
        }

        public bool stop()
        {
            //Uruchamia stop w Grundigu
            return true;
        }

        public bool nagrywaj()
        {
            //Nagrywa na Grundigu
            return true;
        }

        class obslugaMagnetofonow
        {
            private void nagraj(IObsluga_MK magnetofon, TypKasety kaseta)
            {
                //Parametrem metody może być obiekt którego klasa ma zaimplementowany interface IObsluga_MK
                //W tym przypadku także metoda nie ulegnie zmianie po dodaniu nowego typu magnetofonu
                magnetofon.typKasety = kaseta;
                magnetofon.nagrywaj();
            }

            public void rozpocznijNagrywanie()
            {
                nagraj(new Kasprzak(), TypKasety.C60);
                nagraj(new Grundig(), TypKasety.C60);
            }
        }
    }
}

Pozdrawiam,
Zoritt

0

Jestem nowy w te "klocki". Czy za pomocą interfejsu przerobie mój program który obsługuje mysql tak aby obsługiwał też inny przy wyborze opcji z menu? W jaki sposób mogę to zrobić?

0

załóżmy, że na serwerze masz dane które można zapisać jako strukturę:

struct DaneZSerwera
{
  int id;
  string imie, nazwisko;
  int wiek;
  // ...
}

To stwórz sobie interfejs który wygląda np. tak:

interface Baza
{
  IEnumerable<DaneZSerwera> Wszystkie { get; }
  void DodajDoBazy(DaneZSerwera dane);
  void UsunZBazy(int id);
  // tutaj jakies pomocnicze, np.:
  DaneZSerwera ZnajdzPoId(int id);
  DaneZSerwera ZnajdzPoNazwisku(string nazwisko);
  DaneZSerwera ZnajdzPoImieniu(string imie);
}

+ implementacja

class BazaMYSQL : Baza
{
  // tutaj jakies zmienne potrzebne do zainicjowania polaczenia (prywatne) + konstruktor zbierający dane potrzebne do połączenia (uzytkownik, haslo, baza itp.) lub ewentualnie dodatkowa metoda w interfejsie: Polacz(), aczkolwiek wydaje mi sie ze Baza to nie koniecznie musi znajdowac sie na jakims strzezonym haslem polaczeniu, bo moze rownie dobrze znajdowac sie w pliku
  public IEnumerable<DaneZSerwera> Wszystkie
  {
    get
    {
      // tutaj laczysz sie z bazą, dodatkowo możesz np. po wywołaniu tej metody zapisać sobie te wszystkie dane i wiecej nie laczyc sie z serwerem do pobierania danych
    }
  }

 // itd.. 
}

później drugie robisz podobnie. w zaleznosci od tego jakie polaczenie ktos wybral to tworzysz inny obiekt klasy.

Możesz też zastanowić się nad tym, żeby Baza również wymuszała interfejs IDisposable, bo przy bazie ważne jest żeby jednak zwolnic pamiec po duzej ilosci danych

0

Już pojąłem o co chodzi, jednak samo zastosowanie trudno mi sobie wyobrazić na tym co do tej pory pisałem, były to proste aplikacje nie wymagające dużej ilości kodu - muszę je zastosować w przykładach.

1

Możesz też spróbować z "definicji" przez pewien okres czasu dodawać interface do każdej klasy. Pewnie w wielu przypadkach będzie to nadmiarowość ale zaczniesz patrzeć na klasy poprzez ich interface, trochę się uporządkuje kod. Przy tworzeniu obiektów poprzez interface a nie klasę będziesz widział tylko "wystawione" świadomie metody i właściwości. A może ci się uda zastosować ten sam interface do więcej niż jednej kasy :-)

0

używając interfejsów pamiętajcie też o dziedziczeniu. wybór interfejs-klasa abstrakcyjna może być czasem niebanalny.

0
ŁF napisał(a):

używając interfejsów pamiętajcie też o dziedziczeniu. wybór interfejs-klasa abstrakcyjna może być czasem niebanalny.

Jeśli coś definiuje tylko zachowania, to jest interfejsem.
Jeśli coś definiuje abstrakcyjny byt, który posiada cechy współdzielone przez byty pochodne, to jest klasą abstrakcyjną.

1 użytkowników online, w tym zalogowanych: 0, gości: 1