1. Biznes-loqikada interfeyslər: düymələr və hərəkətlər
Bu interfeyslər üzrə sonuncu mühazirədir, ona görə də burada çoxlu praktiki nümunələr olacaq ki, interfeyslərin praktikada necə istifadə olunduğunu daha yaxşı başa düşəsən.
İnterfeyslər xüsusilə böyük tətbiqlərdə dəyərlidir. Ona görə də aşağıda mürəkkəb hallar var, bəzən kurs proqramını qabaqlayır. Maraqlıdırsa — əla! Yoxdursa — növbəti səviyyəyə keç :P
Ən sadə nümunə — düymələr və onlara klikləmək
Tədris konsol tətbiqimizi inkişaf etdirməyə davam edirik — təsəvvür et ki, bizdə sadə istifadəçi elementləri sistemi var (məsələn, düymələr, mətn sahələri və switch-lər) konsol menyusunda.
İstəyirik ki, bəzi elementlər klikləməyə reaksiya versin (düymələr), digərləri isə yox (məsələn, statik başlıq). İnterfeyslər belə ssenariləri elegant şəkildə reallaşdırmağa imkan verir.
Addım 1: İnterfeys yaradırıq
// "Kliklənə bilən" komponent:
public interface IClickable
{
void Click();
}
Addım 2: İnterfeysi müxtəlif element tiplərinə tətbiq edirik
// Bütün menyu elementləri üçün əsas class
public class MenuItem
{
public string Title { get; set; }
public MenuItem(string title)
{
Title = title;
}
public virtual void Display()
{
Console.WriteLine(Title);
}
}
// Düymə: ona "klikləmək" olar
public class Button : MenuItem, IClickable
{
public Button(string title) : base(title) {}
public void Click()
{
Console.WriteLine($"[Düymə] {Title} basıldı!");
}
}
// Sadəcə yazı: klikləmək olmaz
public class Label : MenuItem
{
public Label(string title) : base(title) {}
}
Addım 3: İnterfeys polimorfizmini istifadə edirik
IClickable[] clickableItems = new IClickable[]
{
new Button("Yadda saxla"),
new Button("Çıxış")
// Label burda əlavə etmək olmaz — o IClickable deyil!
};
foreach (var item in clickableItems)
{
item.Click();
}
Gördün magiyanı? Siyahı yalnız “Click” edə bilənləri qəbul edir, yəni uyğun olmayan obyektə “klikləməyə” çalışsan, runtime error almayacaqsan.
2. İnterfeyslər və “Strategiya” pattern-i: alqoritmin yolda seçilməsi
Tutaq ki, sən bir tətbiq yazırsan və orada hesabatları müxtəlif yollarla saxlamaq olar: fayla, verilənlər bazasına, “bulud”a, bəlkə də, Telegram-da şefinə (soruşma, hər şey ola bilər).
Addım 1: Tapşırığı formalaşdırırıq
ReportGenerator class-ı istənilən saxlanma üsulu ilə işləyə bilsin, implementasiyanın detallarını bilmədən.
Addım 2: Strategiya-interfeysi təsvir edirik
public interface IDataSaver
{
void Save(string reportData);
}
Addım 3: Müxtəlif variantları reallaşdırırıq
public class FileDataSaver : IDataSaver
{
public void Save(string reportData)
{
Console.WriteLine("[FileDataSaver] Fayla saxlayıram...\n" + reportData);
// Burada File.WriteAllText(...) üçün kod ola bilərdi
}
}
public class DatabaseDataSaver : IDataSaver
{
public void Save(string reportData)
{
Console.WriteLine("[DatabaseDataSaver] Verilənlər bazasına saxlayıram...\n" + reportData);
// Burada verilənlər bazasına yazmaq üçün kod ola bilərdi
}
}
Addım 4: Saxlama strategiyasını dəyişmək üçün interfeysi istifadə edirik
public class ReportGenerator
{
private readonly IDataSaver _dataSaver;
public ReportGenerator(IDataSaver dataSaver)
{
_dataSaver = dataSaver;
}
public void GenerateReport()
{
string report = "Bu vacib hesabatdır!";
Console.WriteLine("Hesabat yaradılır...");
_dataSaver.Save(report);
}
}
Çevikliyin nümayişi
// ReportGenerator-u yenidən yazmadan saxlanma üsulunu dəyişmək olar!
IDataSaver fileSaver = new FileDataSaver();
ReportGenerator fileReport = new ReportGenerator(fileSaver);
fileReport.GenerateReport();
IDataSaver dbSaver = new DatabaseDataSaver();
ReportGenerator dbReport = new ReportGenerator(dbSaver);
dbReport.GenerateReport();
Praktiki məna: Sabah hesabatları yeni super-müasir servisdə yazmaq istəsən, sadəcə yeni interfeys implementasiyası yazırsan və obyektini arxitekturana əlavə edirsən, mövcud kodun bir sətrini belə dəyişmədən.
3. .NET-də interfeyslər: IDisposable və using
.NET-də ən məşhur interfeyslərdən biri — IDisposable-dır. Onu bütün unmanaged resurslarla işləyən class-lar implement edir: fayllar, stream-lər, şəbəkə bağlantıları.
IDisposable nə üçün lazımdır?
Resursla işləyəndə, onu mütləq azad etmək lazımdırsa (məsələn, faylı bağlamaq), IDisposable implement edirsən və Dispose metodunu yazırsan. Bu, obyektləri using operatoruna verməyə imkan verir, hansı ki, blokdan çıxanda Dispose çağırılmasını təmin edir.
Nümunə: Faylla işin simulyasiyası
public class FakeFile : IDisposable
{
public string FileName { get; }
public FakeFile(string fileName)
{
FileName = fileName;
Console.WriteLine($"Fayl açıldı: {fileName}");
}
public void Dispose()
{
Console.WriteLine($"Fayl bağlandı: {FileName}");
}
}
// Əsas proqramda:
using (var file = new FakeFile("hesabat.txt"))
{
Console.WriteLine("Fayla yazırıq...");
// fayl avtomatik olaraq "bağlanacaq" using-dən sonra
}
Nəticə:
Fayl açıldı: hesabat.txt
Fayla yazırıq...
Fayl bağlandı: hesabat.txt
Həqiqətən faydalıdır: faylı bağlamağı unutmayacaqsan — interfeys və dilin infrastrukturu səni qoruyacaq.
4. Kolleksiyalarda və LINQ-da interfeyslər
Siyahılar, massivlər, dictionary-lərlə işləyəndə, sən artıq interfeyslərdən istifadə edirsən, hətta bunu düşünməsən də.
List<int> list = new List<int> { 1, 2, 3 };
IEnumerable<int> enumerable = list; // hər şey ok!
// İndi elementləri bu cür gəzə bilərsən:
foreach(var x in enumerable)
{
Console.WriteLine(x);
}
LINQ-un əksər metodları kolleksiyalarla IEnumerable<T> interfeysi vasitəsilə işləyir. Bu, kodu konkret kolleksiya tipindən asılı olmadan yazmağa imkan verir.
Niyə lazımdır?
List<T>-ni T[], HashSet<T> və ya hətta öz kolleksiyanla əvəz edə bilərsən — kodun işləməyə davam edəcək!
5. Test üçün interfeyslər (Mock-obyektlər)
Testlərdə kodu xarici asılılıqlardan təcrid etmək çox vacibdir: real bazaya girməmək, real fayllara toxunmamaq. İnterfeyslər stub-lardan (mock-obyektlər) istifadə etməyə və kodu real bazalara və ya fayllara toxunmadan test etməyə imkan verir.
public class FakeDataSaver : IDataSaver
{
public bool WasCalled { get; private set; } = false;
public void Save(string reportData)
{
WasCalled = true;
Console.WriteLine("Mock-obyektdə məlumat saxlanıldı!");
}
}
// Testdə
FakeDataSaver saver = new FakeDataSaver();
ReportGenerator generator = new ReportGenerator(saver);
generator.GenerateReport();
Console.WriteLine($"Save çağırıldı? {saver.WasCalled}");
Nəticə: test real dünyadan asılı deyil, amma lazım olan metodun çağırıldığını yoxlayır!
6. Konfliktlərin həlli üçün interfeyslərin açıq implementasiyası
Bəzən class iki interfeysi eyni metodlarla implement etməlidir, amma bu metodların loqikası fərqlidir.
public interface IFlyable
{
void Move();
}
public interface ISwimmable
{
void Move();
}
public class Duck : IFlyable, ISwimmable
{
// Açıq implementasiya
void IFlyable.Move()
{
Console.WriteLine("Ördək uçur!");
}
void ISwimmable.Move()
{
Console.WriteLine("Ördək üzür!");
}
}
Duck duck = new Duck();
// duck.Move(); // Səhv: belə metod yoxdur!
((IFlyable)duck).Move(); // "Ördək uçur!"
((ISwimmable)duck).Move(); // "Ördək üzür!"
Bu bir az sərt görünə bilər, amma bəzən məhz bunu spesifikasiyalar tələb edir!
7. Event modelində interfeyslər: INotifyPropertyChanged
.NET-də event-ləri dəstəkləmək üçün standart interfeyslər var — məsələn, data modellərində nəsə dəyişəndə və UI bunu bilməlidir (çox vaxt WPF, MAUI və digər GUI-də).
using System.ComponentModel;
public class Person : INotifyPropertyChanged
{
private string name;
public string Name
{
get => name;
set
{
if (name != value)
{
name = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Name)));
}
}
}
public event PropertyChangedEventHandler? PropertyChanged;
}
Məna: istənilən data binding dəstəkləyən framework gözləyir ki, class-ın bu interfeysi implement etsin — və onda UI property dəyişəndə avtomatik yenilənəcək.
8. “Fabrika” (Factory) pattern-də interfeyslər
İnterfeyslər fabriklərin qurulması üçün əladır — müxtəlif, amma bir-birini əvəz edə bilən obyektləri yaradan class-lar.
public interface ITransport
{
void Move();
}
public class Bicycle : ITransport
{
public void Move() => Console.WriteLine("Velosipedlə gedirik!");
}
public class Car : ITransport
{
public void Move() => Console.WriteLine("Maşınla gedirik!");
}
public class TransportFactory
{
public static ITransport Create(string type)
{
return type switch
{
"bike" => new Bicycle(),
"car" => new Car(),
_ => throw new ArgumentException("Naməlum nəqliyyat növü")
};
}
}
ITransport transport = TransportFactory.Create("bike");
transport.Move(); // "Velosipedlə gedirik!"
9. Event-lər üçün interfeyslər: öz event-in nümunəsi
“Event listener” interfeysini təsvir edirik:
public interface ILoginListener
{
void OnLogin(string userName);
}
// Event-i çağıran class
public class LoginManager
{
private List<ILoginListener> listeners = new();
public void Subscribe(ILoginListener listener) => listeners.Add(listener);
public void Login(string userName)
{
Console.WriteLine($"İstifadəçi {userName} daxil oldu.");
foreach (var listener in listeners)
listener.OnLogin(userName);
}
}
// İnterfeysi implement edən class
public class WelcomeMessage : ILoginListener
{
public void OnLogin(string userName)
{
Console.WriteLine($"Xoş gəldin, {userName}!");
}
}
LoginManager manager = new();
manager.Subscribe(new WelcomeMessage());
manager.Login("Vasya");
// İstifadəçi Vasya daxil oldu.
// Xoş gəldin, Vasya!
Müsahibədə: soruşsalar — “öz event sistemini necə reallaşdırardın?”, interfeys-listener-lərdən danış!
10. Plugin-lər üçün interfeyslər (tətbiqlərin genişlənməsi)
Bir çox böyük tətbiqlər plugin-ləri dəstəkləyir. İnterfeyslər sayəsində tətbiqin “yeni modulları” yolda yükləyə bilər, onların içini bilmədən.
public interface IPlugin
{
string Name { get; }
void Run();
}
// Sənin tətbiqin:
public class PluginLoader
{
public void LoadAndRun(IEnumerable<IPlugin> plugins)
{
foreach (var plugin in plugins)
{
Console.WriteLine($"Plugin işə salınır: {plugin.Name}");
plugin.Run();
}
}
}
Plugin-ləri üçüncü şəxslər də yaza bilər — əsas odur ki, interfeysi implement etsinlər. Tətbiqin genişlənə bilən olur!
GO TO FULL VERSION