Generic sınıfların, metotların ve arayüzlerin nasıl oluşturulduğundan (bkz: Generic Sınıflar, Metotlar ve Arayüzler) ve ne tür kısıtlar eklenebileceğinden (bkz: Generic Kısıtlar) bir önceki yazılarımızda bahsetmiştik. Bu içerikte ise Generic’lerin gerçek bir projede ne amaçla ve nasıl kullanılabileceğini olabildiğince yalın ve anlaşılır bir şekilde aktarmaya çalışacağım.
Örnek Senaryo: Müşteri, Ürün ve Kategori bilgilerini tutan Customer, Product ve Category isimli sınıflarımız olduğunu farz edelim. Müşterileri, Ürünleri ve Kategorileri listeleyecek, arama ve silme yapabilecek Manager sınıfları, Generic’leri kullanarak oluşturmaya çalışalım.
public class Customer { public int Id { get; set; } public string FullName { get; set; } public string Address { get; set; } }
Müşterinin Id, isim ve adres bilgilerini tutan Customer isimli bir sınıfımız olsun.
public interface ICustomerManager { List<Customer> List(); Customer Find(int id); bool Remove(Customer customer); }
Müşterileri listeleyecek, silecek ve müşteri listesinde arama yapabilecek metotlara ihtiyacımız olduğunu farz edelim. Bu ihtiyaçları ICustomerManager arayüzünde bildiriyoruz.
public class CustomerManager : ICustomerManager { private static List<Customer> _customers = new List<Customer>() { new Customer { Id = 1, FullName = "Serdar YILMAZ", Address = "Kocaeli" }, new Customer { Id = 2, FullName = "Behzat GÖK", Address = "Sakarya" }, new Customer { Id = 3, FullName = "Sinem ER", Address = "Bolu" } }; public List<Customer> List() { return _customers; } public Customer Find(int id) { return _customers.Find(x => x.Id == id); } public bool Remove(Customer customer) { return _customers.Remove(customer); } }
ICustomerManager arayüzünde bildirimini yapmış olduğumuz metotları, CustomerManager isimli sınıf içerisinde tanımlıyoruz. CustomerManager sınıfında arama, silme ve listeleme işlemlerini _customers koleksiyonu üzerinde yapmaktayım, sizler geliştireceğiniz uygulamalarda bu işlemleri veritabanı üzerinde yapabilirsiniz.
Şimdi ise Ürün ve Kategori bilgilerini tutan Product ve Category sınıflarına ihtiyacımız olduğunu düşünelim.
public class Product { public int Id { get; set; } public string Title { get; set; } public string Explanation { get; set; } } public class Category { public int Id { get; set; } public string Title { get; set; } }
Müşterilerde olduğu gibi Kategorilerde ve Ürünlerde de listeleme, silme ve arama işlemlerini yapacak metotlara ihtiyacımız olacaktır. Bu ihtiyaçları IProductManager ve ICategoryManager arayüzlerinde bildiriyoruz.
public interface IProductManager { List<Product> List(); Product Find(int id); bool Remove(Product product); } public interface ICategoryManager { List<Category> List(); Category Find(int id); bool Remove(Category category); }
Oluşturmuş olduğumuz arayüzleri inceleyecek olursak; her üç arayüzde de(ICustomerManager, IProductManager, ICategoryManager) List, Find ve Remove metotlarının bildiriminin yapıldığını ve sadece parametre türlerinin ve metotların geri dönüş türlerinin farklılık gösterdiğini görebiliriz. O halde Generic bir arayüz oluşturup, parametre türlerini ve metotların geri dönüş türlerini parametrik hale getirebiliriz.
public interface IRepository<T> { List<T> List(); T Find(int id); bool Remove(T entity); }
Generic IRepository<T> arayüzü sayesinde, Manager sınıflar (CustomerManager, ProductManager, CategoryManager) için oluşturduğumuz arayüzlerin (ICustomerManager, IProductManager, ICategoryManager) hepsinde aynı metotların bildirimini yapmak zorunda kalmayacağız. Artık tüm Manager sınıflarda bulunması gereken ortak metotların bildirimini IRepository<T> arayüzünde yapacağız. Manager sınıfa özgü olan metotların bildirimini de o Manager sınıf için oluşturduğumuz arayüzün içerisinde yapacağız.
public interface IRepository<T> { List<T> List(); T Find(int id); bool Remove(T entity); } public interface ICustomerManager : IRepository<Customer> { } public interface ICategoryManager : IRepository<Category> { } public class CustomerManager : ICustomerManager { private static List<Customer> _customers = new List<Customer>() { new Customer { Id = 1, FullName = "Serdar YILMAZ", Address = "Kocaeli" }, new Customer { Id = 2, FullName = "Behzat GÖK", Address = "Sakarya" }, new Customer { Id = 3, FullName = "Sinem ER", Address = "Bolu" } }; public List<Customer> List() { return _customers; } public Customer Find(int id) { return _customers.Find(x => x.Id == id); } public bool Remove(Customer customer) { return _customers.Remove(customer); } } public class CategoryManager : ICategoryManager { private static List<Category> _category = new List<Category>() { new Category { Id = 1, Title="Yazılım"}, new Category { Id = 2, Title="Network"}, }; public List<Category> List() { return _category; } public Category Find(int id) { return _category.Find(x => x.Id == id); } public bool Remove(Category category) { return _category.Remove(category); } }
Kod kalabalığını arttırmamak için yukarıda ki kod bloğunda Product ve ProductManager sınıflarına yer vermedim. Yukarıdaki kod bloklarını inceledikten sonra aklınıza şöyle bir soru gelebilir; ICustomerManager ve ICategoryManager arayüzlerinin içerisinde ekstradan herhangi bir metot bildirimi yapılmadı, o halde neden tanımlama gereği duyuldu, neden IRepository arayüzü direkt Manager sınıflara implement edilmedi ?
IRepository<T> arayüzü içerisinde tüm Manager sınıflarda bulunması gereken metotların bildirimi yapılır. Ancak Manager sınıfların içerisinde IRepository<T> arayüzünde bildirilmiş metotların haricinde kendilerine has metotlar da bulunabilir. Örneğin CustomerManager sınıfı içerisinde müşterilerin adres bilgisini döndüren bir metot olabilir. Eğer IRepository<T> arayüzü içerisinde bu metodun bildirimini yapmış olsaydık, bu metodu CategoryManager içerisinde de gerek olmamasına rağmen tanımlamamız gerekirdi. Bu yüzden tüm Manager sınıflar için ortak olan metotlar IRepository<T> arayüzünde bildirilir, Manager sınıfa özel olan metotlarda o Manager sınıfa özel olarak oluşturulmuş arayüzde bildirilir.
Son olarak; dikkat edecek olursak, IRepository<T> arayüzüne herhangi bir kısıt konulmamış durumda. Bu yüzden arayüze veri tipi olarak hem değer tipliler hem de referans tipliler gönderilebilir. Ancak biz arayüze sadece Customer ve Category gibi Entity’lerin gönderilmesini istiyoruz. O halde IRepository<T> arayüzüne bir kısıt eklememiz gerekiyor.
public interface IEntity { } public interface IRepository<T> where T : class, IEntity { List<T> List(); T Find(int id); bool Remove(T entity); }
IEntity adında bir arayüz oluşturduk ve “where T : class, IEntity“ kısıtı ile IRepository<T> arayüzüne sadece IEntity arayüzü implement alan referans tiplilerin gönderilmesine izin verdik. IRepository<T> arayüzüne Customer ve Category sınıflarının gönderilmesini istediğimizden IEntity arayüzünü bu sınıflara implement ediyoruz. Kod bloklarının son hali;
public interface IEntity { } public interface IRepository<T> where T : class, IEntity { List<T> List(); T Find(int id); bool Remove(T entity); } public class Customer : IEntity { public int Id { get; set; } public string FullName { get; set; } public string Address { get; set; } } public class Category : IEntity { public int Id { get; set; } public string Title { get; set; } } public interface ICustomerManager : IRepository<Customer> { } public interface ICategoryManager : IRepository<Category> { } public class CustomerManager : ICustomerManager { private static List<Customer> _customers = new List<Customer>() { new Customer { Id = 1, FullName = "Serdar YILMAZ", Address = "Kocaeli" }, new Customer { Id = 2, FullName = "Behzat GÖK", Address = "Sakarya" }, new Customer { Id = 3, FullName = "Sinem ER", Address = "Bolu" } }; public List<Customer> List() { return _customers; } public Customer Find(int id) { return _customers.Find(x => x.Id == id); } public bool Remove(Customer customer) { return _customers.Remove(customer); } } public class CategoryManager : ICategoryManager { private static List<Category> _category = new List<Category>() { new Category { Id = 1, Title="Yazılım"}, new Category { Id = 2, Title="Network"}, }; public List<Category> List() { return _category; } public Category Find(int id) { return _category.Find(x => x.Id == id); } public bool Remove(Category category) { return _category.Remove(category); } }
Biraz önce okuduğum Generic Sınıflar, Metotlar ve Arayüzler yazınız icing on the cake is bu da cherry on top oldu. Çok teşekkürler. Gerçek vatanperverler işte böyle ücretsiz içerik paylaşan kişilerdir. Kahvelerde çay içen keko sürüleri değil.