Nie potrafię dobrze zrozumieć interfejsów

0

Mam kilka pytań co do interfejsów. Mam taki programik konsolowy:

public interface ISender
{
    string Send();
}

public class Sms : ISender
{
    public void Send()
    {
        Console.WriteLine("send sms");
    }
} 

public class Email : ISender
{
    public void Send()
    {
        Console.WriteLine("send emial");
    }
}

public class Handler
{
    public void hadle
    {
	ISender sender = new Sms();      

        sender = new Email();
    }
} 


class Program
{
    static void Main(string[] args)
    {
        Handler handler = new Handler();
	handler.handle();
    }
}
 

OUTPUT: send sms send email

Rozumiem po co się używa interfejsów klas abstrakcyjnych, wiem tez co to inversion of control i testy jednostkowe, polimorfizm. Także nie piszę tego bo nie chce mi się poczytać ale po to bo w dalszym ciągu mam z tym problem.

Nie rozumiem w tym przykładzie jaka jest tak naprawdę korzyść z użycia interfejsów.

W przykładzie z interfejsami tworzę dwa obiekty o tej samej nazwie zwracające ten sam interfejs, czyli w sumie jak tworzę nowy obiekt a już istnieje wcześniej obiekt o tej samej nazwie to zastępuje się nowym obiektem

Mogę nie korzystać z interfejsów i zrobić to tak.

public class Handler
{
    public void hadle
    {
	Sms sms = new Sms();      

        Email email = new Email();
    }
}  

class Program
{
    static void Main(string[] args)
    {
        Handler handler = new Handler();
	handler.handle();
    }
}
 

Poza tym że będę musiał tworzyć kolejne nowe obiekty i łamał zasady pisania oprogramowania nie widzę żadnej korzyści z wykorzystania interfejsów. Czy w tym prostym przykładzie są jakieś głębsze korzyści których nie widzę ?

Interfejsy pozwalają się pozbyć zależności, ale pozbędę się ich wtedy jak użyję np DI. W innym przypadku cały czas będę miał te zależności.

I tutaj chyba klucz moich problemów -> Czy tworzenie nowych obiektów które zwracają interfejs, (tak jak w przykładzie) są cały czas zależnością(czyli w klasie Handler)? Bo wiem że teraz przez interfejs się komunikują dwie klasy (Handler i klasy dziedziczące po interfejsie) ale nie widzę korzyści tego..

Prosiłbym was o jakiś prosty przykład który pokazuje że przez to że mam klasy ze sobą ściśle połączone będę miał jakiś problem. A ten problem rozwiąże to że mam interfejsy. Z góry dzięki za pomoc.

0

A teraz wyobraź sobie że masz w tej klasie Handler Listę senderów. I że w ogóle to jest biblioteka która zajmuje się "handlowaniem" a ty jej tylko rejestrujesz odpowiednie Sendery. I wyobraź sobie że np. chcesz napisać własny sender i chciałbyś żeby ten Handler z biblioteki go używał.
Spróbuj zrobić cokolwiek z tego bez interfejsu.

1

Na banalnych przykładach wiele rzeczy to over engineering, ale dlatego, że to są tylko przykłady. I nie masz zwykle planów ich zmieniać czy czegokolwiek robić z nimi w przyszłości.

W prawdziwy świecie przy kodzie produkcyjnym wygląda to inaczej. Często nawet w tak banalnym kodzie używa się dobrych praktyk np interfejsów nie po to żeby mieć korzyść już teraz, ale żeby mieć ją później, jak będziesz np dane rozwiązanie zmieniał, testował (w tym wypadku to "później" może oznaczać 5 minut, lub nawet być "wcześniej" jak piszesz w TDD), czy rozszerzał. Jest łatwiej wtedy, kiedy kod jest dobrze napisany od razu, niż później przerabiać go, kiedy może nie być na to czasu i/lub chęci.

Także nie przejmuj się, że w banalnym przykładzie nie widać sensu, bo go nie ma. Jakby to był kod produkcyjny to byś go zobaczył, zwłaszcza jeśli byś pracował wcześniej z kiepskim.

0

@Shalom, mógłbyś zademonstrować to w postaci kodu/pseudo kodu?

0

Kod może być ten sam który pokazałeś. Dodaj tylko w klasie Handler listę Senderów i metodę register(sender) która dodaje sendera do listy ;]

0

@Shalom zrobiłem coś takiego. Skomentuj to proszę, bo pewnie nie o to Ci chodziło.

public class Handler
    {
        public List<Sender> senders = new List<Sender>();

        public void register(Sender sender)
        {
            senders.Add(sender);
        }

        public void print()
        {
            foreach(var x in senders)
            {
                Console.WriteLine(x.Name);
            }
            Console.ReadLine();
        }
    }

    public class Sender
    {
        public string Name { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Handler handler = new Handler();

            Sender sender = new Sender();
            sender.Name = "SMS";

            Sender sender2 = new Sender();
            sender2.Name = "Email";

            
            handler.register(sender);

            handler.register(sender2);

            handler.print();
        }
    }
 
3

oczywiscie ze nie o to chodzi bo Email jak i SMS ma byc klasa a nie nazwa...

public interface ISender
{
    string Send();
}
 
public class Sms : ISender
{
    public void Send()
    {
        Console.WriteLine("send sms");
    }
} 
 
public class Email : ISender
{
    public void Send()
    {
        Console.WriteLine("send emial");
    }
}
 
public class Handler
{
    private List<ISender> listOfSenders = new List<ISender>();
    public void AddSender(ISender sender)
    {
        listOfSenders.Add(sender);
    }
} 
 
 
class Program
{
    static void Main(string[] args)
    {
        Handler handler = new Handler();
        handler.AddSender(new Sms());
        handler.AddSender(new Email());
        handler.AddSender(new Sms());
    }
} 
4

Nie no generalnie chodziło o to co pokazał @fasadin. Teraz wyobraź sobie że napisałeś bibliotekę do czegośtam np. do monitorowania ataków hakerskich na twój serwer a ten Handler obsługuje mechanizm logowania zdarzeń (tzn jak się coś stanie to wołane jest print na handlerze). Użytkownik może sobie teraz skonfigurować takie Sendery jakie chce, jeden będzie chciał smsy, inny maile a jeszcze inny żeby mu wysłać telegram.
Ale idźmy dalej -> jest klient który chciałby żeby powiadomienie szło do niego za pomocą tranmisji telepatycznej. Kiedy ty pisałeś swoja bibliotekę to w ogóle takiej technologii nie było więc siłą rzeczy standardowe Sendery które udostępniłeś razem z biblioteką tego nie wspierają. Ale przecież Handlera w ogóle nie obchodzi co sie dzieje w Senderze! Ważne tylko żeby Sender udostępniał odpowiedni zestaw metod. Więc mogę sobie napisać nową, własna klasę implementującą ISender i wszystko nadal będzie działać bez problemów.

0

No rzeczywiście, muszę sobie to wszystko jakoś w głowie poukładać.

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