Liskov Yer Değiştirme Prensibi (LSP – Liskov Substitution Principle)

Sıra geldi yazılım tasarım prensipleri (SOLID) makalesinde kısaca değindiğim liskov yer değiştirme prensibini (liskov substitution principle) bir örnekle açıklamaya. Öncesinde bu substitution kelimesini yazmakta baya bir zorlandığımı ve bazı yerlerde kopyala yapıştır ile durumu kurtarmaya çalıştığımı itiraf edeyim. Kendi yazdığım ender yerlerde de yazım yanlışı yapmışsam şimdiden söyleyeyim, kusuruma bakmayın. Yazım açısından bir hayli kafa karıştırıcı kelime, en azından benim için.

Bu prensip ismini vakti zamanında MIT’de (Massachusetts Institute of Technology) profesör olan Amerikalı bir bilim insanı, Barbara Liskov’dan almaktadır. Barbara Liskov, 1987 yılında basılmış olan Data Abstraction and Hierarchy isimli kitabında bu prensiple ilgili tanımı ortaya koymuş ve yazılım dünyasına hiyerarşi ve kalıtım ile ilgili konularda yepyeni bir bakış açısı getirmiştir (Kaynak: wikipedi).

Şimdi Liskov yer değiştirme prensibi (liskov substitution principle) ile ilgili bir örnek yaparak konuyu anlamaya çalışalım. Burada temel olarak yapacağımız şey; bir metodumuz olacak ve bu metod ana sınıf tipinde bir parametre alacak. Bu metoda, ana sınıftan örneklenmiş bir nesne de göndersek, ana sınıftan türetilmiş olan çocuk sınıflardan örneklenmiş bir nesne de göndersek metodun çalışmasında herhangi bir sıkıntı olmamalı. Ayrıca bu metoda da bazı nesneler için istisnai kod yazmamalıyız veya try-catch bloklarını kullanarak hata yakalama işlemlerinde bulunmamalıyız. Sonrasında metodun doğru yazılımının Interface kullanılarak yapılışına ait kodları da vererek bitireceğiz. Tabi bu şekilde sözel anlatım ile konuyu anlayabilmek zor çünkü sözel olarak anlatabilmek de gayet zordu ve ne derecede başarılı olduk bilinmez. Örneğe geçtiğimizde daha anlaşılır bir hale geleceğini düşünüyorum.

Senaryo 1 – Yazılımın Klasik Anlayışa Göre Kodlanması

İlk önce üretim yapan bir işletme olarak aşağıdaki yapıda sınıflarımız olduğunu düşünelim. Ürünlere ait tüm sınıflar (Urun1..N) Urun isimli ana bir sınıftan türetiliyor. Urun isimli ana sınıfımızda da Uret isimli sanal (virtual) bir sınıf tanımlanmış. Türetilen her sınıf bu metodu ezerek (override) kendine ait özel üretim metotlarını işletmektedir.

Aşağıda bir de UrunA isimli bir sınıf daha görülmektedir. O da Urun sınıfından türetilmektedir fakat Uret isimli metodu ezmediğini (override) görüyoruz. Bu ürünü de firmamızda üretilmeyen, başka bir tedarikçiden satın alıp direk olarak kullandığımız veya sattığımız bir ürün olarak düşünebiliriz. Yani bu ürün için üretim yapmıyoruz.

namespace UretimSenaryosu
{
    public class Urun
    {
        public virtual void Uret()
        {
            throw new NotImplementedException();
        }
    }
    
    public class Urun1: Urun
    {
        public override void Uret()
        {
            //Burada ürün 1'e özel üretim metotları çalıştırılıyor.
            Console.WriteLine("Ürün 1'e özel üretim yapıldı.");
        }
    }

    public class Urun2: Urun
    {
        public override void Uret()
        {
            //Burada ürün 2'ye özel üretim metotları çalıştırılıyor.
            Console.WriteLine("Ürün 2'e özel üretim yapıldı.");
        }
    }

    public class UrunA: Urun
    {
    }
}

Tüm sınıflar tanımlandıktan sonra ana projede aşağıdaki gibi bir toplu üretim yapan metodumuz olduğunu düşünelim.

namespace UretimSenaryosu
{
    public class Uretim
    {
        public static void Main(string[] args)
        {
            List<Urun> urunler = UrunListesiGetir();
            foreach (Urun urun in urunler)
            {
                //Normalde burada urun.Uret(); komutu ile işlemi apabiliriz.
                //Fakat ben konunun anlaşılması için üretim işlemlerini yapan bir başka bir sınıfa yönlendirdim.
                UretimYap(urun);
            }
        }

        public static void UretimYap(Urun urun)
        {
            try
            {
                //Burada proje niteliğine göre üretim ile ilgili başka metotlar da çalıştırılabilir.
                urun.Uret();
            }
            catch(Exception hata)
            {
                Console.WriteLine(urun.ToString() + " isimli ürün için üretim yapılamadı.");
            }
        }

        private List<Urun> UrunListesiGetir()
        {
            //Bu metot içerisinde veritabanından ürünlerin bir listesi alındığı düşünülebilir.
            //Ben kolaylık açısından aşağıdaki gibi bir liste döndürerek örnek bir ürün listesi oluşturdum.
            List<Urun> urunler = new List<Urun>();
            urunler.Add(new Urun1());
            urunler.Add(new Urun2());
            urunler.Add(new UrunA());
            return urunler;
        }
    }
}

Proje çalıştırıldıktan sonra konsol ekranında sırası ile aşağıdaki çıktılar görülecektir.

Ürün 1e özel üretim yapıldı.
Ürün 2e özel üretim yapıldı.
UrunA isimli ürün için üretim yapılamadı.

Toplu üretim metodu çalışırken hata veren ürünler için try-catch bloğu kullanarak hata yakalama işlemi yaptık ve uygun bir mesaj verdik. Projenin niteliğine göre başka işlemler veya loglama da yapılabilir. Yada try-catch metodu yerine nesnelerin tiplerine göre bir switch ifadesi kullanılarak uygun işlemler switch blokları içerisinde de yaptırılabilir. Basitlik açısından ben yukarıdaki gibi kullandım.

Bu durumda programın işleyişi açısından belki bir sorun olmadı veya çıkabilecek sorunları bir şekilde yönetebildik fakat mevcut tasarım liskov yer değiştirme prensibine (liskov substitution principle) uygun olmadı. Bu prensip özetle bir ana sınıf, kendisinden türetilen başka bir sınıfla yer değiştirebilir diyordu. Yani Urun1, Urun2 veya UrunA sınıflarından örneklenen nesneler Urun tipinde parametre kabul eden bir metoda gönderildiğinde (bu aşamada üst sınıf ile alt sınıftan örneklenen nesneleri yer değiştirmiş olduk) parametreyi kabul ediyor ve gerekli işlemi yapıyor (Uret metodunu çağırıyor). Bu durumda bizim “UretimYap” metoduna ürünleri gönderdiğimizde hiçbir şekilde “… isimli ürün için üretim yapılamadı.” hatası almamamız gerekiyor.

Senaryo 2 – Yazılım Tasarımının LSP’a Uygun Kodlanması

Peki doğru tasarım nasıl olmalıydı konusuna gelirsek elbette bunun birkaç değişik yolu olabilir. Ben Interface kullanarak yapmayı tercih ettiğimden çözümü de Interface ile yapacağım.

namespace UretimSenaryosu
{
    public Interface IUretimiYapilabilenUrunler
    {
        void Uret();
    }

    public class Urun
    {
    }
    
    public class Urun1: Urun, IUretimiYapilabilenUrunler
    {
        public void Uret()
        {
            //Burada ürün 1'e özel üretim metotları çalıştırılıyor.
            Console.WriteLine("Ürün 1'e özel üretim yapıldı.");
        }
    }

    public class Urun2: Urun, IUretimiYapilabilenUrunler
    {
        public void Uret()
        {
            //Burada ürün 2'ye özel üretim metotları çalıştırılıyor.
            Console.WriteLine("Ürün 2'e özel üretim yapıldı.");
        }
    }

    public class UrunA: Urun
    {
    }
}

Urünlerin tanımlandığı sayfaya “IUretimiYapilabilenUrunler” isimli bir Interface ekledim ve Urunlerin tanımlarında da küçük bir değişiklik yaptım. Ana sınıfımız olan Urun sınıfındaki Uret metodu kaldırıldı ve onun yerine Interface içerisinde Uret isimli bir metot tanımlandı. Alt kısımdaki Urun1 ve Urun2 isimli ürünlere ait sınıflar, bu sınıflardan örneklenen nesneler üretim yapılan ürünler olduğundan ve aynı zamanda da ürün olduğundan hem Urun sınıfından örneklendi hem de yeni arayüzü (interface) uyguladı. UrunA isimli ürüne ait sınıf ise bu arayüzü (interface) uygulamadı ve sadece Urun sınfından örneklendi.

namespace UretimSenaryosu
{
    public class Uretim
    {
        public static void Main(string[] args)
        {
            List<Urun> urunler = UrunListesiGetir();
            foreach (IUretimiYapilabilenUrunler urun in urunler.FindAll(s => s is IUretimiYapilabilenUrunler))
            {
                //Normalde burada urun.Uret(); komutu ile işlemi apabiliriz.
                //Fakat ben konunun anlaşılması için üretim işlemlerini yapan bir başka bir sınıfa yönlendirdim.
                UretimYap(urun);
            }
        }

        public static void UretimYap(IUretimiYapilabilenUrunler urun)
        {
            //Burada proje niteliğine göre üretim ile ilgili başka metotlar da çalıştırılabilir.
            urun.Uret();
        }

        private List<Urun> UrunListesiGetir()
        {
            //Bu metot içerisinde veritabanından ürünlerin bir listesi alındığı düşünülebilir.
            //Ben kolaylık açısından aşağıdaki gibi bir liste döndürerek örnek bir ürün listesi oluşturdum.
            List<Urun> urunler = new List<Urun>();
            urunler.Add(new Urun1());
            urunler.Add(new Urun2());
            urunler.Add(new UrunA());
            return urunler;
        }
    }
}

Programın ana bloğunda yer alan UretimYap isimli metodumuzun parametresini Urun tipinden IUretimiYapilabilenUrunler tipine çevrildi. Main metodunda da foreach içerisinde ürün listesinden sadece IUretimiYapilabilenUrunler arayüzünü uygulayan nesneler seçildi ve UretimYap isimli metoda parametre olarak gönderildi. Bu durumda yazılıma eklenen ürünlerin tek bir merkezden yönetilmesi sağlandı. Bu yapılırken de uygulama, metotlarda ürün tiplerine göre hata yönetimi veya ürün tipine göre yönlendirme (switch) gibi bakımı zorlaştırıcı işlemlerden arındırılmış oldu.

Faydalı olması dileğiyle…

XAF ile İlk Projemizi Geliştirelim

Devexpress eğitim dokümanlarında anlatılan örneklerden bir tanesini bir kaç seri makale ile XAF kullanarak uygulamaya çalışacağım. Öncelikle yapılacak örnek projenin amacı ile başlayalım. Bu projede şirket içi çalışanların Intranet üzerinden etkinliklerini yöneteceği ve diğer verilerinin takibini yapacağı bir uygulama geliştirilmesi hedefleniyor. Ayrıca projenin bir web sitesi bir de winform uygulaması versiyonunun bulunması da hedefleniyor.

  • Bir kullanıcı, etkinlikler için temel bir planlama süreci düzenlemek amacıyla çalışanları, projeleri ve ilgili görev verilerini görüntüleyebilir, arayabilir, filtreleyebilir, yazdırabilir, oluşturabilir, güncelleyebilir ve silebilir.
  • Bir kullanıcı, bir web sitesi için verileri organize etmek ve sunmak amacıyla bir ürün hakkında müşterileri ve yorumları görüntüleyebilir, arayabilir, filtreleyebilir, yazdırabilir, oluşturabilir, güncelleyebilir ve silebilir.

Yazılım Mimarisi

Bu standart üç katmanlı mimari, birçok proje için yeterli bir alt yapı sunmakla birlikte daha karmaşık uygulama mimarileri için bir temel oluşturmaktadır. Takip edeceğimiz bu örneğin basit olması nedeni ile bu mimarinin herhangi bir değişikliğe gerek kalmadan ihtiyaçları karşılayacağı düşünülmektedir. Mimarideki bu başlıkları kısaca aşağıda açıklayarak projeye ilk adımımızı atalım.

  • Data Store / Veri Taban(lar)ı: Uygulama verilerinin depolanacağı veri tabanını temsil etmektedir. XAF popüler birçok veri tabanı yönetim sistemini desteklemektedir. Karmaşık ve özel bazı senaryolarda veri tabanı yerine XML web servisleri de veri kaynağı olarak kullanılabilir.
  • Data Access Layer / Veri Erişim Katmanı (DAL): Bu katmanda veri tabanına erişim için kullanılan alt yapı bulunur. Burada ORM (Object Relational Mapping) aracı olarak Microsoft Entity Framework (EF) veya eXpress Persistent Object (XPO) araçlarından biri seçilebilir.
  • Business Logic / İş Katmanı (BL): Uygulamaya özel çözümleri içeren kodlar burada bulunur. Genel mantığı MVC deseni ile aynıdır. Uygulamalarda UI katmanının direk olarak veri veya veri erişim katmanı ile haberleşmesi istenmediğinden, bu katman içerisinde UI ile veri katmanı arasında iletişimi kuran Controller yapısı da bu katman içerisinde yer alır.
  • Presentation / Sunum Katmanı (UI): Hedef katmana bağlı olarak (windows, web, mobil) iş katmanına veri gönderip sonuçları alan ve kullanıcıya gösteren katmandır. Burada XAF arayüzü kullanmak yerine başka görsel araçlar da kullanılabilir fakat biz konumuz itibari işe XAF’ın kullanışını inceleyeceğiz.

Proje Oluşturma

İlk önce Visual Studio içerisinde “File > New > Project” seçeneği tıklanır ve yeni proje açma ekranı açılır. Burada “Installed > Templates” kırılımı altında “Devexpress XAF” seçilir ve sağ bölümdeki “DevExpress v18.1 XAF Solution Wizard” projesi seçilir (Ben DevExpress v18.1 kullandığım için isimdeki versiyon kısmı v18.1 olarak görünüyor, sizde farklı bir versiyon olabilir). En alttaki “Name” kısmına projemizin adını ve “Location” bölümüne de projeyi açmak istediğiniz klasör yolunu girip OK butonuna tıkladığınızda XAF proje açma sihirbazı karşımıza gelecektir.

Açılan sihirbazın ilk ekranında yazılımın gerçekleştirileceği hedef platformların seçilmesi istenir. Ayrı ayrı platformlar seçileceği gibi, birden fazla platform da seçilerek proje geliştirilebilir. Buradaki platformlardan Mobil seçeneğinin halen beta sürümünde olduğunu ilave edeyim. Bu örnek için mobil platforma derleme yapılmayacağından, Windows ve Web seçeneklerini seçerek devam edeceğiz.

Bir sonraki ekranda veri erişim katmanında (DAL) kullanılacak ORM aracının seçilmesi istenmektedir. Burada sadece bir adet seçilmesi yeterli olacaktır fakat bu örnek kapsamında deneme amaçlı olarak Microsoft firmasına ait Entity Framework (EF) ve Devexpress firmasına ait eXpress Persistent Objects (XPO) araçları birlikte seçilmektedir.

Bu pencerede uygulama için güvenlik seçenekleri yer almaktadır. Burada uygulamaya entegre edilmek istenen güvenlik seçenekleri seçilir. Bu örnek projede güvenlik seçenekleri yer almayacağından, “Authentication” altında yer alan seçeneklerden “None” yazan seçeneği işaretleyerek devam ediyoruz.

Sihirbazın son ekranında da projeye dahil edilmek istenen özel XAF modüllerinin yer aldığı bir ekran gelmektedir. Bu örnek proje kapsamında herhangi bir ek modüle gerek olmadığından ilgili ekranda herhangi bir seçim yapılmadan “Finish” butonuna basılır ve proje oluşturulur.

Proje Ayarlarının Yapılması

Proje açma sihirbazı tamamlandıktan sonra yukarıda görülen “Solution” yapısı otomatik olarak oluşturulur. Kısaca bu projelerin ne işe yaradıklarından bahsedelim ve veritabanı bağlantı ayarlarının yapılması işlemine geçelim.

  • SimpleProjectManager.Module, platformdan ve UI katmanından bağımsız olan ve diğer katmanlar tarafından ortak kullanılan bir XAF modülüdür. Bu paylaşılan modülde yapılan değişiklikler, bu modüle bağlı olan diğer modüllere otomatik olarak yayılmaktadır.
  • SimpleProjectManager.Module.Win, Windows Forms projesine özel kodları ve Windows platformuna bağımlı kodları içerebilen bir XAF modülüdür. Bu modül SimpleProjectManager.Module projesini referans alır. SimpleProjectManager.Module içerisinde yapılan değişiklikler
    SimpleProjectManager.Module.Win projesine otomatik olarak yansır.
  • SimpleProjectManager.Module.Web, ASP.Net web formlarına özel kodları ve Web ortamına bağımlı kodları içerebilen bir XAF modülüdür. Bu modül SimpleProjectManager.Module projesini referans alır. SimpleProjectManager.Module içerisinde yapılan değişiklikler 
    SimpleProjectManager.Module.Web projesine otomatik olarak yansır.
  • SimpleProjectManager.Win ve SimpleProjectManager.Web sırasıyla Windows masaüstü ve Web tarayıcı arayüzlerini sağlamak için paylaşılan ve platform bağımlı modülleri referans alan uygulamaları yazmak için hazırlanmış projelerdir.

Proje açıldığında SimpleProjectManager.Win içerisinde app.config ve SimpleProjectManager.Web içerisinde de web.config dosyaları hazır vaziyette varsayılan bağlantı parametreleri ile bulunmaktadır. Projeyi çalıştırdığınızda bu config dosyalarında belirtilen veritabanlarına ulaşamayacağı için proje hata verecek ve açılmayacaktır. Yukarıda belirttiğim her iki config dosyasında bulunan connection string bilgilerini aşağıda görülen şekilde değiştiriniz.

<connectionStrings>
    <add name="EasyTestConnectionString" connectionString="Integrated Security=SSPI;Pooling=false;Data Source=YourServerIP;Initial Catalog=YourDatabaseName;User Id=YourUserID; Password=YourPassword"/>
    <add name="ConnectionStringXPO" connectionString="Integrated Security=SSPI;Pooling=false;Data Source=YourServerIP;Initial Catalog=YourDatabaseName;User Id=YourUserID; Password=YourPassword"/>
    <add name="ConnectionStringEF" connectionString="Integrated Security=SSPI;MultipleActiveResultSets=True;Data Source=YourServerID;Initial Catalog=YourDatabaseName;User Id=YourUserID; Password=YourPassword" providerName="System.Data.SqlClient"/>
</connectionStrings>

Config dosyalarında gerekli değişiklik yapıldıktan sonra Solution Explorer penceresinde SimpleProjectManager.Win (veya SimpleProjectManager.Web) projesine sağ tıklayarak Set as Startup Project seçeneğini işaretleyin. Sonrasında projeyi çalıştırırsanız projenin açılış ekranı karşınıza gelecektir. Şu an herhangi bir modül içermeyen boş bir ekran olarak uygulamamız çalıştı. Bir sonraki makalede gerekli modüllerin eklenmesi ile işlemleri ele alacağım.

Faydalı olması dileğiyle…

React Native Öğrenmeye Başlayalım

Son zamanlarda giderek popüler hale gelen mobil uygulama geliştirme alt yapılarından biri olan react native, facebook tarafından geliştirilmekte olan bir javascript framework’tür. Proje hakkında tüm detaylı bilgiler https://facebook.github.io/react-native/ adresinden elde edilebilir. Ben de öğrenmeye buradaki eğitim dökümanlarını takip ederek başladım ve öğrendikçe de buraya notlarımı aktaracağım. İlk önce kurulum işlemleri ile başlayalım.

Bu arada aşağıdaki anlatımlar ve bundan sonraki makalelerin tümünde Windows ortamında geliştirme yapılacağının varsayılacağını belirtmek istiyorum.

Bağımlılıklar ve Gereksinimlerin Kurulması

React native ile uygulama geliştirmek için bilgisayarımızda ilk önce Node.js kurulumu yapılmış olması gerekmektedir. Ek olarak react native komut satırı arayüzü, phyton2, JDK (Java Development Kit) ve nihayetinde yazılan kodların Android veya iOS ortamına derlenebilmesi için Android Studio ve/veya XCode.

React’ın sitesinde kurulum işlemlerinin Chocolatey ile yapılmasını öneriyor. Biz de o şekilde devam edelim. Windows’ta başlat butonuna tıklayın ve metin kutusuna PowerShell yazın. Gelen programlar içerisinden ‘Windows PowerShell’ yazan program üzerine sağ tıklayarak ‘Yönetici Olarak Çalıştır’ seçeneğini işaretleyin (yoksa normal şekilde tıklayıp çalıştırabilirsiniz). Açılan mavi ekranlı kod satırına aşağıdaki kodu kopyalayıp aynen yazın ve çalıştırmak için Enter tuşuna basın.

Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))

Chocolatey kurulumundan sonra chocolatey komut setlerini Windows komut satırını kullanarak çalıştırabiliriz. Windows komut satırını açmak için başlat butonuna tıklayın ve metin kutusuna CMD yazın. Gelen siyah ekran üzerine sağ tıklayarak ‘Yönetici Olarak Çalıştır’ seçeneğini işaretleyin. Komut satırı ekranı açıldıktan sonra Node.js, JDK ve Phyton 2 kurulumuna geçebiliriz. Aşağıdaki kodu kopyalarak komut satırı ekranına yapıştırın ve ardından Enter tuşuna basın ve kurulumların tamamlanmasını bekleyin.

choco install -y nodejs.install python2 jdk8

Kurulumların tamamlanmasının ardından React-Native CLI kurulumu için aşağıdaki kodu da Windows komut satırına yapıştırın ve çalıştırın. Aşağıdaki kodun çalışması için komut satırını kapatıp yeni bir komut satırını açmanız gerekebilir.

npm install -g react-native-cli

Android Geliştirme Ortamının Kurulması

Ben android yazılımı gerçekleştireceğim için Android Studio kurulumu ile devam edeceğim. Belki ilerleyen zamanlarda iOS için de gerekli geliştirme ortamının kurulumuna değinebiliriz. Android studio kurulum adımlarımlarını ‘Android Studio Kurulumu‘ isimli makalede bulabilirsiniz. Konuyu uzatmamak adına bu makalede android kurulumuna değinmeyeceğim.

Son adım olarak da react-native komutlarının tüm klasörlerde çalışabilmesi yani Microsoft Komut Satırı programı (meşhur DOS ekranımız) tarafından tanınabilmesi için bir tanımlama yapılması gerekiyor. Aşağıdaki görselde Windows 7 işletim sisteminde bu ayarın nereden yapılacağına ait bir ekran görüntüsü bulunmaktadır. Denetim masası > Sistem > Gelişmiş Sistem Seçenekleri > Gelişmiş > Ortam Değişkenleri yolunu izleyerek, açılan pencerede ANDROID_HOME isimli bir değişken tanımlanır ve değer olarak “C:\Users\WindowsKullanıcıAdınız\AppData\Local\Android\Sdk” değeri girilir (burada sembolik olarak yazdığım
WindowsKullanıcıAdınız ibaresi yerine oturum açmış olduğunuz Windows kullanıcı adınızı yazmalısınız).

Klasik Hello World Uygulamamızı Yapmaya Hazırız!

React-native komutlarını kullanarak uygulama oluşturmak için ilk önce Windows komut satırı uygulaması açılmalıdır. Burada istediğiniz bir klasörün içerisine gelerek (ben D:\Projeler içerisine oluşturacağım) aşağıdaki kod yazılmalı ve enter tuşuna basılmalıdır. Enter tuşuna basıldıktan sonra yeni proje oluşturma işlemi bilgisayarınızın ve Internet bağlantınızın gücüne göre birkaç dakika içerisinde tamamlanacaktır.

react-native init HelloWorld

Uygulamamızı Test Edelim…

Uygulamayı kendi telefonumda, USB üzerinden ADB (Android Debug Bridge) ile test edeceğim. Bunun için bazı ayarlar yapılması gerekiyor. İlk önce Windows komut satırında ADB komutlarını çalıştırabilmek için bu bağlantı üzerinden adb ve fastboot kurulumları indirilmelidir. İndirilen zip dosyasını herhangi bir klasöre açtıktan sonra bu klasörün içerisindeki “platform-tools” klasörüne ait yolun PATH ortam değişkenine eklenmesi gerekmektedir. Aşağıdaki görselde benim bilgisayarımdaki ekleniş şekli yer almaktadır (bir üstteki görselde bu ortam değişkenleri ekranına nasıl gelineceğinden bahsettim).

Yukarıdaki ayarlama işleminin tamamlanmasının ardından Android işletim sistemi yüklü olan telefonunuzu react-native yüklemiş olduğunuz bilgisayara USB arayüzü ile bağlayın ve komut satırından aşağıdaki ADB komutunu çalıştırın.

adb devices

Bu komut çalıştırıldığında bilgisayarınıza bağlı olan cihazların bir listesini verir. Eğer cihazınız bu listede görünmüyorsa cihazınızda geliştirici seçeneklerini açmalı ve buradan USB Debugging seçeneğini aktif hale getirmelisiniz. Geliştirici seçeneklerinin açılması için telefonun ayarlar bölümünde telefon hakkında, yazılım bilgisi, cihaz hakkında, sistem bilgisi, vb isimler altında (her cihazda farklı olabilir) bir bölüm bulunmaktadır. Bu bölüme girdiğinizde android build numarası görülecektir. Bu build numarasına 7 kez tıkladığınızda telefonun geliştirici seçenekleri aktif duruma gelmiş olacaktır. Tekrar ayarlar bölümüne gittiğinizde önceki menülere ilaveten geliştirici seçenekleri isminde yeni bir bölümün eklendiğini göreceksiniz. Bu bölüme giriş yaparak USB Debugging seçeneğini aktif hale getirebilirsiniz. Aktif hale getirme işleminin ardından tekrar “adb devices” komutunu çalıştırdığınızda cihazınıza ait Id numarasının listede görünmüş olması gerekiyor.

Artık uygulamamızı cihaz üzerinde test etmeye hazırız. Windows komut satırından HelloWorld uygulamasının bulunduğu klasöre giderek aşağıdaki komut çalıştırıldığında uygulama derlenecek ve kısa süre içerisinde cihaza yüklenecektir.

react-native run-android

Buraya kadar react-native ortamının kurulumunu, android studio ortamının kurulumunu, çalıştırmak için gerekli ayarları ve ilk uygulamamızı gerçekleştirmiş olduk.

Faydalı olması dileğiyle…

Android Studio Kurulumu

Bu makalede Andoid Studio kurulumunu adım adım gerçekleştirmeye çalışacağım. Aşağıdaki kurulum adımları 32 bit Windows 7 işletim sistemi üzerine yapılan ve Android Studio 32 bit versiyonuna ait kurulumu göstermektedir.

İlk önce Andoid Studio sitesinden program kurulumunu indirerek başlayalım. İşletim sistemi 64 bit olan ve Android Studio 64 bit sürümünü kurmak isteyenler için 2 seçenek mevcut. Sitedeki EXE versiyonu indirip klasik bir kurulum yapmak veya ZIP versiyonu indirip kuruluma gerek kalmadan istediğiniz bir klasöre dosyaları çıkartıp, bin klasörü altındaki studio64.exe ile direk kullanmak.

32 bit Windows üzerine kurulum yapmak isteyenler için ise sadece ZIP seçeneği bulunmakta. Benim de bilgisayarım 32 bitlik Windows 7 işletim sistemine sahip olduğu için ben de Android Studio 32 bit versiyonuna ait ZIP versiyonunu indirdim ve bin klasörü altındaki studio.exe’yi (32 bit için bu isim) kullandım.

Kurulum Adımları

Program ilk açılışında geliştirme ortamı ile ilgili bazı ayarların yapıldığı bir kurulum ekranı karşımıza gelmektedir. Bu ekranda bir karşılama mesajı ve kısa bir tanıtım yazısı yer almaktadır. Bu ekranda Next butonuna basılarak kurulum işlemini başlatılır.

Kurulum tipinin seçildiği ikinci ekranda Standart ve Custom seçenekleri yer almaktadır. Standart seçilerek devam edildiğinde varsayılan paket ve parametrelerle bir kurulum gerçekleştirmektedir. Biz burada Custom seçeneğini işaretleyerek devam edeceğiz.

Sonrasında karşımıza SDK bileşenlerinin seçildiği ekran gelmektedir. Burada, yaptığımız uygulamayı test etmemizi sağlayan Android Virtual Device ve Performans için Intel HAXM isimli bir yazılım için kurulum seçenekleri bulunmaktadır. HAXM, Android Virtual Device (AVD)’ın yavaşlığını gidermek ve olabildiğince hızlandırmak için kullanılan bir yazılımdır. Bununla ilgili detaylı bilgi edinmek isteyenler Internet üzerinden arama yapabilirler. Bu ekrandaki tüm seçenekleri işaretliyoruz ve Next butonuna basarak bir sonraki ekrana geçiyoruz.

Emulator ayarları sayfasında ise emulator ve HAXM uygulaması için bilgisayarınızdan ayırmak istediğiniz maksimum RAM bellek boyutunu girmeniz isteniyor. Ben önerilen ayarda (512 MB) bırakıp devam ediyorum. Eğer çok fazla kaynağınız varsa buradaki bellek miktarını artırıp emulator performansını artırabilirsiniz.

Aşağıdaki ekranda ise buraya kadar yapmış olduğumuz tüm ayarların bir özeti yer almaktadır. Burada bizden kurulum için son bir onay istemektedir. Burada Finish butonuna basarak gerekli dosyaların indirilmesi ve kurulması aşamasına geçilmektedir.

Finish butonuna basıp kuruluma geçildiğinde, yukarıdaki aşamalarda seçilen tüm bileşenlerin Internet üzerinden indirildiği ve kurulduğu bir kurulum aşamaları bilgilendirme ekranı gelmektedir. Tüm indirme ve kurulum işlemlerinin tamamlanması Internet hızı ve bilgisayar özelliklerine göre değişiklik gösterebilir.

Kurulum işleminin tamamlanmasından sonra Android Studio açılış ekranı karşımıza gelmektedir. Yeni proje oluşturma, var olan projeyi açma gibi yazılımla ilgili birçok işlem bu ana ekrandan başlatılabilmektedir.

Neden Oreo 8.0?

Kurulum adımları yukarıdaki aşamaya kadar takip edildiğinde aslında android uygulama geliştirme için gerekli geliştirme ortamının en son versiyonu kurulmuş hale ve hemen yeni proje açılıp kodlamaya başlanabilir hale gelinmektedir. Bazı durumlarda sıfırdan yeni bir projeye başlamak yerine elinizde var olan eski bir sürüme destek vermek ve orada geliştirme yapmak durumunda kalabilirsiniz. Ya da yazacağınız yeni uygulamanın hedef kitlesine göre daha alt versiyon kullanan kullanıcılara ulaşmak ve toplam kullanıcı havuzunu büyütmek adına uygulamanızı eski bir versiyonda oluşturmak isteyebilirsiniz. Benim elimdeki oreo 8.0 platformuna özel yazılmış kodlar için bu SDK’yı da indirmem gerekmektedir. Bunun için yukarıdaki ekranda bulunan Configure butonuna tıklayıp açılan menüden de SDK Manager butonuna tıklamak ve aşağıdaki ekranı açmak gerekmektedir.

Yukarıdaki ekranın sağ alt köşesinde bulunan Show Package Details seçeneği işaretlendiğinde SDK’lara ait alt detaylar da listeye gelmektedir. SDK Platforms isimli ilk tab içerisinde yüklenmek istenilen paketin altındaki minimum 3 seçeneğin (Android SDK Platform 26, Sources for Android 26, Google APIs Intel x86 Atom System Image) seçilmesi gerekmektedir. Tabi 64 bitlik versiyona sahipseniz bu kurulum seçeneklerinde de 64 bit olan seçeneklerin seçilmesi gerekmektedir.

Sonrasında SDK Tools tabına geçilir ve buradan da SDK platform 26’ya ait olan tool versiyonlarından uygun olanı (benim örneğimde 26.0.3) seçilir ve Apply tuşuna basılarak Internet üzerinden indirme ve kurulum işlemleri başlatılır.

Sonrasında ihtiyaç oldukça açılış sayfasındaki Configure > SDK Manager adımından gerekli platform yükleme işlemleri yapılabilir.

Faydalı olması dileğiyle…

XAF Eğitim Serisi

Devexpress firmasına ait yazılım geliştirme platformu olan XAF Application Framework hakkında çok fazla Türkçe içerik olmadığından bir XAF eğitim içeriği yazmak istiyorum. Bunu yaparken de en önemli kaynağım https://documentation.devexpress.com sitesi olacak. Bu seride yer alacak olan içerikler bir bakıma Devexpress firması tarafından hazırlanan İngilizce içeriklerin Türkçeye çevrilmesi şeklinde olacak. Tabi kendi yorumumu da anladığım kadarı ile eklemeye çalışacağım. İlk önce XAF kullanmanın ve kullanmamanın getireceği avantaj ve dezavantajlarla başlayalım.

Genel Bakış

Günümüzde şirketler operasyonlarından kaynaklanan verileri bünyelerinde tutmak ve gerektiğinde ilgililere raporlamak durumunda kalmaktadır. Gitgide artan ve sürekli değişen ortamda yazılımların da bu sürece hızlı bir şekilde adapte olması gerekmektedir. Şimdi şirketlerin yazılımlarını tedarik ettiği yöntemlere göre geleneksel tedarik yöntemlerinin ve XAF alt yapısının avantaj ve dezavantajlarını inceleyelim.

İş Modeli 1: Şirkete Uygun, Özel Yazılım Geliştirilmesi

Şirket, ihtiyaç duyduğu yazılımları bağımsız bir yazılım şirketine veya varsa kendi şirket bünyesindeki bilgi teknolojileri departmanına sıfırdan yazdırabilir. Eğer yazılımcılar uygulamaları bazı işleri kolaylaştıracak şekilde bir çatı (framework) üzerine inşa etmemişse muhtemelen aşağıdaki sorunlarla karşılaşacaktır.

  • Yazılım geliştiriciler veritabanındaki her bir tablo veya alana veri girişin sağlamak için bir ekran tasarımı yapmak zorunda kalacaklar. Bu durumda çok basit veri giriş ve raporlama ekranları için bile uzun bir zaman ayırmak zorunda kalacaklar.
  • Çok büyük olasılıkla kod tekrarı yapılmaması konusu göz ardı edilecektir. Özellikle birden fazla yazılımcı olması durumunda herkesin kendi tarzını yansıtması sonucu, kod okunabilirliği düşecek ve sonradan projeye dahil olan yazılımcılar için sıkıntılı bir süreç yaşanacaktır.
  • Her yazılımda olabilecek hataların sıfırdan geliştirilen bir yazılımda görülmesi çok daha büyük bir olasılıktır. Hele ki yeterli testler yapılmadıysa çok daha büyük hatalar çalışma zamanında ortaya çıkacak ve geri dönülmeyen sorunlara yol açabilecektir.
  • Testlerin yapılması için çok ciddi zaman ve insan kaynağı ayrılması gerekir. Eğer yeteri kadar özen gösterilmezse hatalar gözden kaçacak ve ileride büyüyerek karşımıza çıkacaktır.
  • Bu tür uygulamalarda yazılım prensipleri büyük ihtimalle göz ardı edilmiş olacağından sonradan genişletilebilirlik ve bakım süreçleri çok maliyetli duruma gelebilir. Programda istenen yeni bir özellik için tüm formların tekrardan gözden geçirilmesi ve düzenlenmesi gerekir. Bu da çalışan bazı kodların bu düzenleme esnasında hataya düşmesine neden olabilir.

Bu yaklaşımda bazı avantajlar da bulunmaktadır.

  • Bu tür özel programlarda standart yazılımların sağlamayacağı kadar detaya inilebilir ve özelleştirilebilir. Tüm kontrol yazılım ekibindedir.
  • Uygulamalar belli bir kalıba uymak zorunda değildir. Örneğin eklenecek bir özelliğin, uygulamayı kullanacak tüm şirketlerin ortak ihtiyacını karşılayacak daha genel bir yapıda olması gibi kısıtlara takılmazlar. Bu yüzden işletmeye özel operasyonları tüm detaylarına kadar yönetebilirler.

İş Modeli 2: Evrensel Bir Yazılım Ürünü Satın Alma

Piyasada kendini kanıtlamış ve ihtiyacı karşılamaya aday yazılımlar arasından bir tanesi (Microsoft Dynamics Ürünleri, SAP, Micro, Logo, vb.) seçilerek satın alma yoluna gidilebilir. Bu iş modelinde karşılaşılacak bazı dezavantajlar şunlardır.

  • Bu tip uygulamaların kendine özgü frameworkleri, kodlama yapıları hatta özel yazılım dilleri bile olabilir. Bu da yazılım alındıktan sonra destek kısmında ya bu dilin/yapının öğrenilmesi veya bilen kişilerin istihdam edilmesi gibi pahalı bir yöntem olarak karşımıza çıkabilir.
  • Uygulamaların özelleştirilmesine ve özel ihtiyaçlara göre genişletilmesine ya izin vermez yada sınırlı bir esnekliğe sahiptir. Bazı durumlarda bu genişleme işi için de yüksek maliyetler karşımıza çıkabilir.
  • Bu evrensel ürünler sektördeki ihtiyaçların çoğunluğuna karşılık vermek durumunda olduklarından performans olarak özel yazılımlara göre biraz daha yavaş kalabilir. Ayrıca hali hazırda kullanmayacağınız bir çok özelliği de bünyesinde barındırdıklarından kullanmadığınız özelliklerin maliyetine de katlanma durumunda kalınabilir.

Tabi bu yazılımların avantajları da yok değil. Bunları da kabaca şu şekilde sıralayabiliriz.

  • Piyasada bilinen, kendini ilgili konuda kanıtlamış bir yazılım satın aldığınızda kalitesinden emin olabilirsiniz. Kapsamlı analizler sonucu yazıldığından ve gerekli testleri yapıldığından, hatalarla karşılaşma riskiniz diğer yazılımlara göre oldukça düşüktür.
  • Destek ve ek geliştirme maliyetlerini de göze alıp satıcı firma ile sözleşme imzalandığında, firma bünyesinde yazılımcı ve destek personeli tutmayarak sabit maliyetleri düşürebilirsiniz.

İş Modeli 3: eXpressApp Framework Kullanmak

Aslında tam olarak bir iş modeli denemese de yine de dokümandaki modellemeye sadık kalmak için XAF’ı da ayrı bir başlık altında değerlendireceğiz. Bu iş modeli aslında hazır bir alt yapı kullanarak yazılımları bu alt yapı ile geliştirme modeline tekabül eder ve yukarıda bahsettiğimiz iki modelin arasında bir noktaya konumlandırılabilir. Tabi makalenin konusu XAF olduğu için, XAF’ın özellikleri ve sağladığı kolaylıklar ile yazımıza devam edeceğiz.

XAF, uygulamaların sıfırdan yazılması durumunda karşılaşılacak problemleri gidermek için (İş modeli 1’i açıklarken bu problemlere değinmiştik) tasarlanmış bir alt yapıdır. Bazı avantajlarını şu şekilde sıralayabiliriz.

  • Arayüz ile kaygılanmayıp direk olarak iş mantığına odaklanıldığından farklı platformlar (WinForms, ASP.NET ve Mobile) için tasarlanmış uygulamalarda aynı iş mantığını kolayca yeniden kullanabilirsiniz. Bunu kabaca vergi ve ek maliyetlerin hesaplamasını yapan metodun bir kez yazılıp her platform için bu metodun çağrılması şeklinde düşünebiliriz.
  • Veri tarama ve düzenleme için birçok benzer formun manüel olarak oluşturmanıza gerek kalmaz. Bu şekilde uygulamaların bakımları da kolay yapılabilir bir duruma gelir çünkü verileri değiştirdiğinizde, çok sayıda formu ve/veya web sayfasını değiştirmek zorunda kalmazsınız. Tüm bu formlar ve web sayfaları veriye göre otomatik olarak XAF tarafından oluşturulur.
  • Verilere erişmek için herhangi bir DBMS ile uğraşmak veya ADO.NET kullanmak zorunda kalmazsınız. eXpressApp Framework, veri yönetimi için daha üst düzey bir araç sunar. Bu araç, verilerinizi ayrıntılı olarak tanımak zorunda kalmadan, herhangi bir popüler DBMS’yi kullanarak verilerinizi kaydetmeyi kolaylaştırır.
  • Sadece verileri depolamak ve gerektiğinde görüntülemek için tasarlanmış iş uygulamaları oluşturmayı son derece kolaylaştırır.
  • Uygulamaların herhangi bir .NET dili kullanılarak oluşturulmasına izin verir.
  • Arayüzlerin ve standart davranışların tamamını kendinize göre özelleştirebilirsiniz. Hatta açık kaynak kodlu geldiği için alt yapının tamamını da özelleştirmeniz mümkündür (pratikte çok zor olsa bile).
  • XAF tarafından sağlanan tüm bileşenler çok fazla test edilerek geliştirildiği için güvenle kullanılabilir.

Sonuç

Anladığım kadarı ile iş modellerinin avantaj ve dezavantajlarını aktarmaya çalıştım. Tabi başka iş modelleri olabileceği gibi, yukarıda aktarılanlardan daha fazla avantaj ve dezavantaj da eklenebilir. Burada hem Devexpress dokümanlarına sadık kalmaya çalıştım hem de anladığım kadarı çevirerek basit ilaveler de yapmaya çalıştım.

Faydalı olması dileğiyle…

Açık – Kapalı Prensibi (OCP – Open Closed Principle)

Bu serinin ilk makalesi olan yazılım tasarım prensipleri (SOLID) içerisinde kısa tanım olarak bahsettiğim açık – kapalı prensibini (open – closed principle) örneklendirerek açıklamak istiyorum.

Yukarıdaki görselde müşterilerin genel davranışlarından biri olan projeden sürekli talepte bulunma isteği resmedilmiş. Müşteri açısından bakıldığında birçok talep yapıyor olmak gayet olağan bir davranıştır. Çünkü gerçek hayatta bir fabrika veya başka bir kurum müşteriniz olabilir ve bu kurumlar da insanlar gibi yaşayan birer organizma gibidir. İş yapış şekli, yöntemler, ürünler, tasarımlar ve daha sayamadığım bu gibi konular zaman içinde sürekli değişime uğrar. Yazılımın da bu talepleri hızlı ve esnek bir biçimde karşılıyor olması beklenir.

Bir uygulama senaryosu üzerinden devam edelim. Örneğimizde çeşitli ürünler üreten bir fabrikamız olduğunu düşünelim. Bu fabrikanın piyasa ve müşteri ihtiyaçlarını karşılamak amacı ile her ay onlarca yeni tip ürünü piyasaya sürdüğünü düşünelim. Şimdi senaryoyu açık – kapalı prensibine (open – closed principle) uymayan şekilde kodlayalım ve programın ilerleyen zamanlarda yaşayacağı problemleri görelim.

İlk aşamada fabrikada üretilen ürünlerin tanımlanması ile başlayalım. Aşağıdaki kodda 2 adet ürün tanımlandı fakat buradaki ürünlerin sayısı istenildiği kadar çoğaltılabilir.

namespace UretimSenaryosu
{
    public class Urun1
    {
        public void Uret()
        {
            //Burada ürün 1'e özel üretim metotları çalıştırılıyor.
            Console.WriteLine("Ürün 1'e özel üretim yapıldı.");
        }
    }

    public class Urun2
    {
        public void Uret()
        {
            //Burada ürün 2'ye özel üretim metotları çalıştırılıyor.
            Console.WriteLine("Ürün 2'ye özel üretim yapıldı.");
        }
    }
}

Yukarıda görülen “Uret” metodu sembolik bir metottur. Bu metot içerisinde sadece o ürüne özel bazı akışlar, o ürüne özel kurallar, varsa özel hesaplamalar ve/veya ürüne özel olarak şu an aklıma gelmeyen diğer her türlü işlevlerin bulunduğunu düşünebiliriz. Tabi ki bunlar sektöre göre değişim gösterecektir.

İkinci aşamada fabrikadaki ürünleri üreten Fabrika isimli bir sınıfımız olduğunu düşünelim ve bu sınıfı kodlayalım.

namespace UretimSenaryosu
{
    public class Fabrika
    {
        public void Urun1Uret(Urun1 urun)
        {
            //Ürün 1 üretiliyor.
            urun1.Uret();
        }

        public void Urun2Uret(Urun2 urun)
        {
            //Ürün 2 üretiliyor.
            urun2.Uret();
        }
    }
}

Son olarak da Program.cs içerisindeki kodları yazalım.

namespace UretimSenaryosu
{
    public class Uretim
    {
        public static void Main(string[] args)
        {
            Urun1 urun1 = new Urun1();
            Urun2 urun2 = new Urun2();

            Fabrika fabrikam = new Fabrika();

            fabrikam.Urun1Uret(urun1);
            fabrikam.Urun2Uret(urun2);
        }
    }
}

Senaryo 1 – Üretim Hattına Yeni Bir Ürün Eklendi.

Buraya kadar basit anlamda bir üretim senaryosu oluşturduk. Şimdi ise üretim hattımıza Ürün 3 isimli yeni bir ürün geldiğini düşünelim. Bu yeni ürüne göre programımız aşağıdaki gibi şekillenecek. İlk önce yeni ürüne ait sınıfımızı oluşturuyoruz.

namespace UretimSenaryosu
{
    public class Urun1
    {
        public void Uret()
        {
            //Burada ürün 1'e özel üretim metotları çalıştırılıyor.
            Console.WriteLine("Ürün 1'e özel üretim yapıldı.");
        }
    }

    public class Urun2
    {
        public void Uret()
        {
            //Burada ürün 2'ye özel üretim metotları çalıştırılıyor.
            Console.WriteLine("Ürün 2'ye özel üretim yapıldı.");
        }
    }

    public class Urun3
    {
        public void Uret()
        {
            //Burada ürün 3'e özel üretim metotları çalıştırılıyor.
            Console.WriteLine("Ürün 3'e özel üretim yapıldı.");
        }
    }
}

Sonrasında ise üretim işlemini yapan Fabrika sınıfımıza Ürün 3 için gerekli üretim kodlarını ekliyoruz.

namespace UretimSenaryosu
{
    public class Fabrika
    {
        public void Urun1Uret(Urun1 urun)
        {
            //Ürün 1 üretiliyor.
            urun1.Uret();
        }

        public void Urun2Uret(Urun2 urun)
        {
            //Ürün 2 üretiliyor.
            urun2.Uret();
        }

        public void Urun3Uret(Urun3 urun)
        {
            //Ürün 3 üretiliyor.
            urun3.Uret();
        }
    }
}

Son olarak da Program.cs içerisine Ürün 3 için üretim yapma komutunu veriyoruz.

namespace UretimSenaryosu
{
    public class Uretim
    {
        public static void Main(string[] args)
        {
            Urun1 urun1 = new Urun1();
            Urun2 urun2 = new Urun2();
            Urun3 urun3 = new Urun3();

            Fabrika fabrikam = new Fabrika();

            fabrikam.Urun1Uret(urun1);
            fabrikam.Urun2Uret(urun2);
            fabrikam.Urun3Uret(urun3);
        }
    }
}

Peki her ürün eklendiğinde Fabrika sınıfımızı bu şekilde değiştirerek her yeni ürün için bir üretim yapma kodu mu gireceğiz. Elbette işler bu şekilde de yürüyebilir fakat bu açık – kapalı prensibine (open – closed principle) oldukça ters bir mimari oluşturmaktadır. Her seferinde tüm kodların elden geçmesi belli bir zaman sonra programın bakımında zorluklar çıkaracak ve işler iyice karmaşık hale gelecektir. Oysa ki yeni bir ürün eklendiğinde sadece ilgili yeni ürüne ait sınıfımızı (model olarak da bilinir) eklesek ve üretim yapan Fabrika isimli sınıfa hiç dokunmasak nasıl olurdu?

Senaryo 2 – Aynı Kodun Açık – Kapalı Prensibine (Open – Closed Principle) Göre Yazılması

Açık – kapalı prensibine (open – closed principle) göre kodlama işlemi soyut sınıflar (abstract class) kullanılarak da yapılabilir fakat ben arayüz (interface) uygulamalarını daha çok sevdiğim için bu yöntemle yapacağım. İlk Aşama olarak tekrar iki adet ürün sınıfımızı yazalım. Bu sefer öncekinden farklı olarak bir üretim arayüzü yazacağız ve tüm sınıfların bu arayüzü uygulamasını sağlayacağız.

namespace UretimSenaryosu
{
    public interface IUrun
    {
        void Uret();
    }

    public class Urun1: IUrun
    {
        public void Uret()
        {
            //Burada ürün 1'e özel üretim metotları çalıştırılıyor.
            Console.WriteLine("Ürün 1'e özel üretim yapıldı.");
        }
    }

    public class Urun2: IUrun
    {
        public void Uret()
        {
            //Burada ürün 2'ye özel üretim metotları çalıştırılıyor.
            Console.WriteLine("Ürün 2'ye özel üretim yapıldı.");
        }
    }
}

Sınıflarımız bir arayüzü uygulayacak şekilde tanımladıktan sonra üretim işleminin yapıldığı Fabrika sınıfını da açık – kapalı prensibine (open – closed principle) göre yazalım. Farkedileceği üzere artık Fabrika sınıfının içerisinde sadece bir adet IUrun tipinde parametre kabul eden bir metodumuz var ve bu metot tüm ürünlerin üretimini yapabiliyor.

namespace UretimSenaryosu
{
    public class Fabrika
    {
        public void Uret(IUrun urun)
        {
            urun.Uret();
        }
    }
}

Son olarak da Program.cs içerisindeki kodları açık – kapalı prensibine (open – closed principle) göre düzenleyelim. Buradaki değişiklikte de görüleceği üzere üretilecek tüm ürünleri Fabrika sınıfı içerisindeki Uret metoduna gönderdik. Buradaki Uret metodu artık ürün bağımsız şekilde kendisine gönderilen her ürün için üretim yapabiliyor.

namespace UretimSenaryosu
{
    public class Uretim
    {
        public static void Main(string[] args)
        {
            Urun1 urun1 = new Urun1();
            Urun2 urun2 = new Urun2();

            Fabrika fabrikam = new Fabrika();

            fabrikam.Uret(urun1);
            fabrikam.Uret(urun2);
        }
    }
}

Senaryo 3 – Üretim Hattına Yeni Bir Ürün Eklendi.

Şimdi bu yeni kodlama yapısında fabrikadaki üretim hattına yeni bir ürün eklendiğinde yapılacak işlemleri aşağıda gösterelim. Her zamanki gibi ilk aşama olarak ürünümüzü ekliyoruz.

namespace UretimSenaryosu
{
    public interface IUrun
    {
        void Uret();
    }

    public class Urun1: IUrun
    {
        public void Uret()
        {
            //Burada ürün 1'e özel üretim metotları çalıştırılıyor.
            Console.WriteLine("Ürün 1'e özel üretim yapıldı.");
        }
    }

    public class Urun2: IUrun
    {
        public void Uret()
        {
            //Burada ürün 2'ye özel üretim metotları çalıştırılıyor.
            Console.WriteLine("Ürün 2'ye özel üretim yapıldı.");
        }
    }

    public class Urun3: IUrun
    {
        public void Uret()
        {
            //Burada ürün 3'e özel üretim metotları çalıştırılıyor.
            Console.WriteLine("Ürün 3'e özel üretim yapıldı.");
        }
    }
}

Fabrika sınıfımızda ve içerisindeki Uret metodunda herhangi bir değişiklik yapmıyoruz. Program.cs içerisine de ürün 3 için üretim yapma kodunu yazıyoruz ve projemizin içerisine yeni ürünümüzü başarı ile eklemiş oluyoruz.

namespace UretimSenaryosu
{
    public class Uretim
    {
        public static void Main(string[] args)
        {
            Urun1 urun1 = new Urun1();
            Urun2 urun2 = new Urun2();
            Urun3 urun3 = new Urun3();

            Fabrika fabrikam = new Fabrika();

            fabrikam.Uret(urun1);
            fabrikam.Uret(urun2);
            fabrikam.Uret(urun3);
        }
    }
}

Görüldüğü üzere projeye yeni ürünü eklemek için sadece ürün tanımlarını içeren sınıfın tanımlanması yeterli oldu. Projede ne kadar fazla karmaşık yapı olursa olsun, proje bu prensibe göre kodlandığında bizim için ileriki bakım aşamaları sancılı olmaktan çıkacaktır.

Faydalı olması dileğiyle…

Tek Sorumluluk Prensibi (SRP – Single Responsibility Principle)

Bugünkü yazımda daha önce bahsetmiş olduğum yazılım tasarım prensiplerinden (SOLID) tek sorumluluk prensibi (single responsibility principle) üzerinde duracağım. Yukarıdaki görselde görüleceği gibi bazı durumlarda içerisinde birçok işlev barındıran sınıflar (class) aynı bir isviçre çakısı gibi size pratiklik sağlayabilir. Fakat işler büyümeye ve daha karmaşık hale gelmeye başladığında bu işlevler güçsüz kalmaya ve sizi bazı noktalarda kilitlemeye başlar.

Bu konuyu basit bir şekilde örneklendirecek olursak loglama, mail gönderme ve kimlik doğrulama işlemlerini kendi üzerine toplamış bir sınıf kodlayabiliriz.

public class Siparis
{
    public void SiparisKaydet()
    {
        if (KimlikDogrula())
        {
            try
            {
                 // Sipariş kaydetme işlemleri burada yapılıyor

                 MailGonder("Siparişiniz başarı ile kaydedildi", ...);
            }
            catch (Exception e)
            {
                 Log("Sipariş kaydedilirken hata oluştu.", e, ...);
            }
        }
    }

    public bool KimlikDogrula()
    {
        // Kimlik doğrulama ile ilgili işlemler...
    }

    public void MailGonder(string mesaj, ...)
    {
        // Mail gönderimi ile ilgili işlemler...
    }

    public void Log(string mesaj, Exception e, ...)
    {
        // Loglama ile ilgili işlemler...
    }
}

Yukarıdaki örnekte görüleceği üzere sipariş sınıfımız, siparişi kaydetmenin yanı sıra siparişi kaydeden kişinin kimlik doğrulama işlemleri, sipariş kaydedildiğinde ilgililere mail gönderilmesi ve eğer bir hata ile karşılaşılırsa çıkan hatanın loglanması gibi fazladan bir takım görevler de üstlenmiş durumda. Bu fazladan görev üstlenme durumu bizim sipariş sınıfımızı olması gerekenden daha kalabalık bir hale getirdiği gibi, kimlik doğrulama, mail gönderimi ve loglama işlemlerinin diğer sınıflarda kullanılmasını engelleyerek kod tekrarlarına neden olmaktadır.

Şimdi bu örneği tek sorumluluk prensibine (single responsibility principle) göre böldüğümüzde oluşacak yeni halini aşağıya kodlayalım.

public class Siparis
{
    public void SiparisKaydet()
    {
        if (GuvenlikIslemleri.KimlikDogrula())
        {
            try
            {
                 // Sipariş kaydetme işlemleri burada yapılıyor

                 MailIslemleri.MailGonder("Siparişiniz başarı ile kaydedildi", ...);
            }
            catch (Exception e)
            {
                 LoglamaIslemleri.Log("Sipariş kaydedilirken hata oluştu.", e, ...);
            }
        }
    }
}

// Diğer sınıfları static olarak tanımladım. Projenin genel yapısına göre bu durum değişebilir.

public static class GuvenlikIslemleri
{
    public static bool KimlikDogrula()
    {
        // Kimlik doğrulama ile ilgili işlemler...
    }
}

public static class MailIslemleri
{
    public static void MailGonder(string mesaj, ...)
    {
        // Mail gönderimi ile ilgili işlemler...
    }
}

public static class LoglamaIslemleri
{
    public static void Log(string mesaj, Exception e, ...)
    {
        // Loglama ile ilgili işlemler...
    }
}

Bu örnekte görüldüğü üzere sipariş sınıfımız artık sipariş ile ilgili olmayan kimlik doğrulama, mail gönderme ve loglama işlemlerini kendi içinde barındırmıyor, bu işlemler için ilgili diğer sınıflardaki metotları çağırıyor. Bu şekilde sipariş sınıfımız sadece kendi işini yapan sade bir sınıf haline geldi, kod kalabalığından arındı. Ayrıca diğer işlemler de ayrı sınıflar içerisine taşındığından projenin her yerinde kullanılabilir duruma geldi ve böylece kod tekrarının da önüne geçilmiş oldu.

Faydalı olması dileğiyle…

Yazılım Tasarım Prensipleri

Hayatta başarılı olmak için nasıl ki bazı prensiplere sahip olmak gerekiyorsa, bana göre bir yazılım projesinin de bazı prensiplere dayalı olarak geliştirilmesi gerekiyor. Tabi söz konusu olan bir sektör ise her sektörde olduğu gibi yazılım sektöründe de bu prensiplerin belli standartlar kapsamında yapılması en doğal olanıdır. Bu yazının kapsamında daha önce araştırmış olduğum fakat unuttuğum bir konu olan yazılım tasarım prensipleri (SOLID) kavramına tekrardan bir göz atacağım ve notlarım arasına eklemiş olacağım.

İlk önce SOLID kelimesini oluşturan kavramları ve bunların kısa tanımlarını incelemek istiyorum. Sonrasında her bir konu için ayrı bir makale hazırlayarak konuları örnekler ile detaylandırmaya çalışacağım.

(S) Single Responsibility Principle

Özetle bir sınıfın (class) sadece bir işten sorumlu olmasıdır. Örneğin iş emri ile ilgili bir sınıf tanımladığımızı düşünürsek, bu sınıftaki metotlar sadece iş emri ile ilgili olmalıdır. Söz gelimi iş emrinin durumunun değiştirilmesi, açılması gibi işlemler bu sınıf tarafından yapılırken, bu hareketlerin loglanması, bildirimlerinin ilgili kişilere gönderilmesi gibi işler başka sınıflar tarafından yapılmalıdır. Konu ile ilgili detay makaleye buradan ulaşabilirsiniz.

(O) Open-Closed Principle

Genel kabul gören slogan tipinde bir tanımı vardır. Bir sınıfın genişletilmeye açık olması (Open) fakat değiştirilmeye kapalı olması (Closed) prensibidir. Bu prensibe göre projenin ileri safhalarında doğacak ihtiyaçlarının var olan sınıflardaki kodlara ekleme yapılarak veya kodlarda değişiklik yapılarak karşılanması bu prensibe aykırıdır. Var olan sınıf kendisinde hiçbir yazılımsal değişiklik yapılmadan, belli bir arayüzü (Interface) uygulayan ve sonradan eklenebilecek tüm diğer nesnelerle uyumlu çalışabilmelidir. Konu ile ilgili detay makaleye buradan ulaşabilirsiniz.

(L) Liskov Substitution Principle

Temel anlamda kalıtım ile türetilen nesneler (instance) ile tüm diğer nesnelerin türetildiği ana nesne birbiri ile yer değiştirildiğinde aynı davranışı sergilemeleri prensibine dayanır. Yani türetilen sınıftan örneklenmiş nesneyi ana sınıf tipinden bir parametre alan metoda gönderdiğimizde hata alıyorsak veya hata almamak için hata yakalama bloklarını kullanarak bazı sınıflara özel istisnai kodlar yazıyorsak, tasarımda bu prensibi göz ardı etmişiz demektir.

(I) Interface Segregation Principle

Uygulamamızda sınıfların uygulaması gereken arayüzler birbiri ile ilişkili özelliklerine göre gruplandırılmalıdır. Bu aşamada çok fazla üyesi olan bir arayüz tasarlamak yerine bir sorunu çözecek ve sadece o soruna odaklanmış özellik ve metotları içeren arayüzler oluşturulmalıdır.

(D) Dependency Inversion Principle

Projelerdeki üst seviye nesnelerin daha alt seviyede işlem yapan nesnelere olan bağımlılığının alt sınıftan üste doğru değil de üst sınıftan alta doğru ilerlemesi prensibine karşılık gelir. Bu tür bağımlılıklar sınıfın kendisi ile değil de arayüzlerle yapılmak sureti ile alt seviye sınıflar ile bağlantısı gevşek hale getirilmiş olur.

Bu makale konuya tanım boyutunda bir giriş makalesi formatında oldu. Bazı özellikleri örneklerle açıklamadığım için anlaşılması güç olmuş ve tanımlara boğulmuş bir makale havası yansıtmış olabilirim. Bu sorunu ileride her bir konuyu ayrı birer makale olarak ele alıp örneklendirerek çözmeyi planlıyorum. Ana hedefim makaleleri olabildiğince kısa tutmak. Bu yüzden tüm konuları detaylı olarak burada anlatmak yerine ayrı ayrı bölümlendirmeyi daha cazip buluyorum.

Faydalı olması dileğiyle…

Opel Alırken 1 Değil 1000 Kez Düşünün

Internet üzerinde buna benzeyen çok sayıda şikayet var ve ben bir benzerinden müzdarip olduğum konuyu buraya yazmak istedim. Her ne kadar başlıkta araç marka adı belirtmiş olsam da bu makalede anlatmak istediğim Opel araçların teknik olarak kötü olması veya olmaması değil. Olay tamamen satış sonrası hizmetler, müşteri memnuniyeti ve servis kalitesi bağlamında değerlendirilmesi gereken bir konu. Aşağıda yaşadıklarım bilfiil kendi tecrübe ettiğim bir sorundur. Tabi yine yorum ve karar size ait. Bu arada sizi fazla sıkmamak adına süreçleri olabildiğince hızlı geçeceğim. Gerçek hayatta 3 aylık bir döneme tekabül ediyor zira.

Gelelim konuya. 1500 kilometrede 2. El olarak Toyota 2. el üzerinden almış olduğum araçta bir hafta sonrasında sağa çekme problemi olduğunu fark ettim. Bunun üzerine vakit kaybetmeden yetkili servise (Bursa-Neskar Otomativ) götürerek ücreti ile rot-balans ayarı yaptırdım fakat yaklaşık 10 gün sonra tekrar sağa çekmeye başladı.

Rot-balans ayarlarının bu kadar kısa bir sürede -üstelik düz yolda evden işe işten eve giderken- bozulamayacağını düşündüğüm için işin biraz daha ciddi olabileceğinden şüphelendim ve Opel Türkiye genel merkezinden bir şikayet kaydı açarak ve konu ile ilgilenilmesini talep ettim. Genel merkez beni tekrar Neskar Otomotive yönlendirdi. Kontrol ettiler ve sorunun ön takımlardan olabileceğini söyleyerek ön tekerleklerin yerini değiştirdiler ve tarafıma geri verdiler. O gün işim nedeni ile mesai bitiminde uğrayabildiğim için aracı güvenlik görevlisinden teslim aldım. Aldığım araçla daha eve giderken bariz bir şekilde aracın sola çektiğini fark ettim ve ertesi gün tekrar götürdüm. Yapılacak hiçbir şey kalmamışçasına tekerleklerin yeri tekrar eski haline getirildi ve araç sağa çeker vaziyette bana iade edildi.

Şikayetimi yenilemem üzerine 20! yıllık tecrübeli servis müdürü ve 2 çalışan ile sürüş testine çıktık fakat yol boyunca aracın sağa çektiğini kendisine anlatamadım (Opel, kötü niyetli bir politika izleyerek, bilinçli olarak hata kabul etmek istemiyor diye düşünüyorum). Arada arkadaki tamirci arkadaşlar benim dediğime gelecek oluyor fakat müdürden çekindiklerinden midir nedir en son hepsi müdürün teşhisinde mutabık kalıyor. Opel’e ait test aracında teste çıktığımızda ise aracın yol durumuna göre sağa veya sola çektiğini gördük. Bu normal zaten ama benim aracımda araç sola hiç çekmiyor. Aynı rotadan geçtiğimizde araç biraz sola doğru çekme durumunda kaldığında bile birkaç yüz metre içerisinde tekrar kendi şeridine geçiyor ve daha da izin verilirse (direksiyona dokunmazsam) sağ şeride doğru ivmeleniyor (tahmini 2km çapında bir daire çizme niyetinde). Zaten araba düz giderken direksiyon ile gösterge paneli paralel çizgi haline de gelmiyor, hafif sağa doğru eğimli görünüyor. Asla tam bir sola gitme yok. Şimdi de ücretli olarak tamire verirseniz bakalım diyorlar. Yapacaklarına güvenmediğim için vermiyorum tabi ki.

Benim işimin tek çözüm yolu kaldı o da mahkeme yolu. Tabi Türkiye’de bu yol ne kadar tercih edilebilir bilmiyorum, mahkeme süreci ile uğraşmak zor. Peki buraya niçin yazıyorum, insafa gelip çözerler diye mi? Hayır. Opel alacak olanlar, özellikle Bursa’da oturanlar tekrar düşünsün diye.

Hello Blog World!

Merhaba!

Esasen çok defadır denediğim fakat bir türlü tembelliğimi yenip vakit ayıramadığım blog sitemi yeni arayüzü (ve yeni sunucusu) ile tekrar yayına almış bulunmaktayım.

Bu sefer kendime büyük hedefler koymuyorum. Amacım sadece yazılıma dair karşılaştığım ve akılda kalması zor olan konuları kendimce not almak olacak. Bu yazılar bazen ileri seviye konular olabileceği gibi bazen de çok basit olabilecek.

Eğer yapabilirsem de yukarıdaki konuya ilave olarak yeni öğrenmeye başladığım konulardaki öğrenme serüvenimi de not ederek hem kendime bir hatırlatıcı hazırlamak hem de Internet üzerinde kaynak oluşumuna katkıda bulunmak istiyorum.

Haydi hayırlısı…