15 Temmuz 2010 Perşembe

Nesne Yönelimli Programlama Temel Kavramlar

Nesne Yönelimli Programlama Kavramları 

- Kalıtım, Polimorfizm, Compositon
- Temel OOP Terimlerinin Anlaşılması (is-a ve has-a ikişkileri)
- OOP Mimarilerinin Başarısı
- Nesne ve Sınıf Kavramları

Nesne Yönelimli Programlama Prensipleri

- Single Responsibility Principle
- Open Closed Principle
- Liskov Substitution Principle
- Dependency Inversion Principle
- Interface Segregation Principle
- Reuse Release Equivalency Principle
- Common Closure Principle
- Common Reuse Principle
- Acyclic Dependencies Principle
- Stable Dependencies Principle- Stable Abstractions Principle

Nesne Yönelimli Programlamanın Temel İlkeleri
Veri Gizleme
Sınıf ve Nesne
Yordamlar ve İletiler
Çokbiçimlilik
Arayüz Oluşturma
Türetme ve Çokbiçimlilik
Erişim denetimi
Liskov Yerine Geçme İlkesi - LSP (Liskov Substitution Principle)
Açık Kapalı İlkesi - OCP (Open-Closed Principle)
Bağımlılık Ters Çevirme İlkesi - DIP (Dependency Inversion Principle)
Arayüz Ayırma İlkesi – ISP (The Interface Segregation Principle)
Sürüm Yeniden Kullanımı Eşdeğerlik İlkesi – REP (The Release Reuse Equivalency Principle)
Ortak Kapatma İlkesi – CCP (The Common Closure Principle)
Ortak Yeniden Kullanım İlkesi – CRP (The Common Reuse Principle)
Çevrimsiz Bağımlılık İlkesi – (ADP) (The Acyclic Dependencies Principle)
Kararlı Soyutlamalar İlkesi – SAP (The Stable Abstractions Principle)
Kararlı Bağımlılıklar İlkesi – (SDP) (The Stable Dependencies Principle)

Tasarım Kalıpları

Aynı problemler sürekli bir yazılımcının karşısına farklı farklı zamanlarda çıkmaktadır ve bu sorunlara aynı çözümler üretilebilmektedir. Bu noktada tasarım kalıplarına ihtiyaç duyulmaya başlanmıştır çünkü tasarım kalıpları hem aynı problemi tekrar tekrar çözmemizin önüne geçer hem de bir sorun için üretilebilinecek en optimal çözümü kullanmamızı sağlar. Bu ayrıca programımızda hata çıkma olasılığını da azaltır ve daha güçlü bir kod yazmamızı sağlar. Bu ayrıca yazılımın en önemli prensiplerinden biri olan yeniden kullanılabilirlilik kavramını da destekler.

Tasarım Kalıpları’nın Dünyadaki Genel Tanımlanma Biçimi
İsim: Her kalıbın genel olarak tek bir adı vardır ve bu ad başka bir kalıpta kullanılamaz
Amaç: Kalıbın amacı
Problem: Kalıbın çözdüğü problem
Çözüm: Kalıp probleme nasıl bir çözüm yaklaşımı getirmektedir?
Birimler: Kalıpta kullanılan birimler.(UML gösterimi ile birlikte)
Gerçekleştirim: Kalıbın kodları

Genel olarak  tasarım kalıpları programlama dillerinden bağımsız olarak tanımlansalar da, nesneye yönelimli programlama dillerine uygun tasarım kalıpları daha çok bilinir. Bu kalıplar, nesneler ve sınıflar arasındaki ilişkileri ve etkilişimleri gösterirler. Programcı bir tasarım kalıbını elindeki soruna bakarak özelleştirip kullanabilir.


Bu kalıpları uygulayabilmek için kulanılması gereken prensipler

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

Yazılmış bir modülün genişlemeye açık ama değişikliğe kapalı olması gerekliliğini vurgular. Yani yazılan kod üzerine bir eklenti yapılmak istendiğinde önceden yazılmış kodlarda değişiklik gerekliliği olmamalıdır. Gerekli eklentiler sadece eklenti için gereken yeni özelliğin kodlanması ile sonuçlanmalıdır.

Liskov Ayrışabilme Prensibi (Liskov Substitution Principle)

"Türemiş sınıflar, türedikleri temel sınıfların yerini herhangi bir problem çıkmadan alabilmelidir." prensibidir. Yani temel sınıftan yaratılmış bir nesneyi kullanan bir fonksiyon, temel sınıf yerine bu temel sınıftan türemiş bir sınıftan yaratılmış bir nesneyi de aynı yerde kullanabilmelidir.

Bağımlılığın Azaltılması Prensibi (Dependency Inversion Principle)

Kullanıcı ile sınıflar arasındaki ilişkinin olabildiğince soyutlanmış yapılar üzerinden yapılmasını önerir. Yani tasarımda ilişkilerin gerçek sınıflardan türemiş nesneler ile değil, ilgili arayüzler(interface) ve soyut sınıflar kullanılarak gerçeklenmesi gerekir.

Tek Sorumluluk Prensibi (Single Responsibility Principle)

"Bir sınıf sadece tek bir sorumluluğu yerine getirmelidir ve yerine getirdiği sorumluluğu iyi yapmalıdır." prensibidir. Her sınıf sadece kendisi ile ilgili tek bir sorumluluğu yerine getirir. Her sınıfın sorumluluğu farklı olduğu zaman, değişmesi için tek bir sebep olur,o da ihtiyaçların değişmesidir. Sınıfların birden fazla sorumluluğunun olması bağımlılığın artmasına neden olur.

Arayüz Ayırma Prensibi (Interface Segregation Principle)

Kullanılacak methodların ve özelliklerin gruplandırılarak her biri ayrı işlevi tanımlayan farklı arayüzlere bölünmesini savunur. Sonuç olarak, bir sınıfın kullanmadığı method ve özellikler programın içeriğine alınmamış olur.

Tasarım kalıplarını inceleyecek olursak temelde 3 ana gruba ayırabiliriz:

Yaratılış Kalıpları (Creational Patterns): Nesnelerin nasıl yaratılacağı hakkında alternatifler sunar. Farklı durumlarda yaratılması gereken nesnelere karar verir.

Yapısal Kalıplar (Structural Patterns): Nesneleri ve sınıfları birleştirerek karmaşık kullanıcı arayüzleri gibi daha geniş yapılar oluşturur.

Davranışsal Kalıplar (Behavioral Patterns): Nesne grupları arasındaki iletişimin tanımlanmasında kullanılır ve daha karmaşık programlarda akış kontrolünü sağlar. Nesnelere işlevsel sorumluluklarını atar.

Tasarım kalıplarının listesi:

Davranışsal Kalıplar
Chain of responsibility
Command
Interpreter
Iterator
Mediator
Memento
Observer
State
Strategy
Template method
Visitor

Yapısal Kalıplar
Adapter
Bridge
Composite
Decorator
Facade
Flyweight
Proxy

Yaratılış Kalıpları

Abstract Factory
Builder
Factory Method
Prototype



 

Javada class içerisinde 'static' kullanımı.

Javada metodları static yaptığımız zaman bu bize instance yaratmadan o metodu kullanmamızı sağlar. Aynı şekilde bu değişkenler için de geçerli, sınıf içinde bir değişkenin static olması bizim bu değişkene instance yaratmadan erişmemizi sağlar. 
Örneğin:
       public class Araba{
              public static int a;
              public int b;
       }

Araba.a //diyerek a değerine erişebiliriz ancak aynı şekilde Araba.b diyerek b değişkenine erişmemiz mümkün değil. Bunun için bir instance yaratmamız gereklidir.
Araba bmw=new Araba();
bmw.b //diyerek b değişkenine erişebiliriz.

Static metodlara örnek olarak da aşağıdaki örneği verebiliriz

public class Araba {

      static void hiz1()
      {
             System.out.println("hiz1 kullanılıyor");
      }

      void hiz2()
      {
             System.out.println("hiz2 kullanılıyor");
      }
}

//Main içerisinde Araba.hiz1() diyerek hiz1 metoduna instance yaratmadan erişebiliriz ancak hiz2'ye aynı şekilde erişmemiz mümkün değil.

İnner classlar için durum biraz farklıdır. Bir inner class'ı static yapsak da yapmasak da bu class'a instance yaratmadan erişebiliriz. Örneğin:

public class Araba {

      public class Teker1{

      }

      public static class Teker2{

       }
}

Araba.Teker1 ve Araba.Teker2 diyerek her iki class'a erişebiliriz.

Not: Static metodların içerisinde static olmayan değişkenler kullanamayız. Bunun nedeni static olarak tanımlanan herşeyin ilk olarak oluşturulmasıdır. Bu nedenle static bir metodun içerisinde static olmayan bir değişken kullanmaya çalıştığımız zaman o değişken henüz oluşturulmadığından derleyici hata mesajı verecektir.

9 Temmuz 2010 Cuma

Javada Interface ve Abstract Classlar Arasındaki Farklar

  • Interfaceler çoklu kalıtımı sağlamaya yardımcı abstract classlar ise çoklu kalıtımı desteklemez.
  • Interfacelerde metodların içerisini dolduramayız ama abstract classlarda doldurabiliriz Böylece bütün alt sınıfların belli bir özelliğe sahip olmasını sağlayabiliriz.
  • Interface ile yapabildiğimiz herşeyi hatta daha fazlasını abstract classlar ile de yapabiliriz.
  • Eğer türeteceğimiz classlarda belli başlı varsayılan özellikleri tekrar tekrar kopyala-yapıştır yapmak istemiyorsak o zaman abstract class kullanmamız gerekir. Çünkü abstract classlarla bir metodu tüm alt classlarda varsayılan metod şeklinde tanımlayabiliriz ve alt classlarda bunları tekrar yazmamıza gerek kalmaz kalıtımla aktarılmış olur.
  • Kalıtım sağlamak istiyorsak abstract classlar kullanmamız gerekir.
  • Abstract classları kullanmak hız açısından avantaj sağlar.
  • Interface de yeni bir metod yazdığımız zaman bu interfaceden implement ettiğimiz tüm classlarda bu metodun içini tek tek doldurmak gerekiyor ancak abstract classlarda durum farklıdır burada bir metod tanımlayıp içini doldurduğumuzda abstract sınıfımızdan türetilmiş bütün sınıflar bu özelliği kazanmış olur. 

8 Temmuz 2010 Perşembe

Java'da Interface Kullanılmasının Nedenleri

Java da interface kullanmamızın en büyük nedeni interfaceden türetilecek sınıflar için bir standart(zorunluluk da diyebiliriz) belirlemektir. Örneğin Telefon adında bir interface üretiyoruz ve  daha sonra interfaceden sınıflar implement ediyoruz. Bütün bu sınıfların bazı ortak özelliklere sahip olmasını bir zorunluluk haline getirebiliyoruz. Örneğin interfacede  arama() adında bir metod yazıyoruz ( içini boş bırakmak zorundayız interfacelerde) ve türetilecek bütün sınıflar kendi özelliklerine uygun olarak arama() metodunu dolduruyor. Böylece bütün telefonların arama özelliğine sahip olmasını bir standart haline getirmiş oluyoruz. 

Veritabanıyla ilgili bir örnek de vermek istiyorum. Örneğin çok geniş bir yazılım yazdığımızı varsayalım. Kullanıcıya bir veritabanı seçtirip buna göre işlemler yaptırmak istiyoruz. O zaman kodumuzda şöyle  satırlar olacaktır:
if(secim=="MySQL")
Veritabani vt =new MySQL();
if(secim=="ORACLE")
IVeritabani vt = new ORACLE();

.... bir kaç tane daha olabilir...

Daha sonra..  vt.baglan()  diyerek baglantimizi sagliyoruz.
Biz daha önce veritabanlarini tek bir Veritabani adlı interfaceden implement etmiştik ve orada baglan() adinda bir metod tanımlayarak içini boş bırakmnıştık ve tüm veritabanlarinin içine kendi özelliklerine uygun olarak baglan() metodunu farklı şekillerde tanımlamıştık.
Böylece hepsinde baglan() metodunun olmasını garanti etmiştik.

Eğer böyle yapmasaydık mesela mySQL için vt.connect() yazmamiz gerekirdi ve bu kodumuzda gereksiz uzamalara neden olabilirdi. Interfaceyle bunun önüne geçmiş oluyoruz.