14 Nisan 2015 Salı

OOP de önemli kavramlar

OOP nedir?
OOP yani object-oriented programming, nesne kavramı üzerine inşa edilen bir programlama yöntemidir.
Bunun amacı veri ile kodun birbirine nesne üzerinden entegre edilmesidir.

OOP'den önce veri ile kod birbirinden ayrılmaıdığından gitgide büyüyen programlar karmaşıklaşmaya başlamıştı. Bu yüzden veri ile onu işleyen kodun sistemli bir yaklaşım ile birleştirilmesi ihtiyacı ortaya çıktı.

OOP de programlar, kendi bünyesinde veri saklayan, ve birbiri ile metodları aracılığıyla iletişimde bulunan nesneler şeklinde tasarlanır.

Nesne nedir?
Nesne dediğimiz şey soyut bir veri yapısıdır. 
Bu veri yapısı içinde hem veri (durum bilgisi - state), hem de kod (davranış bilgisi - behaviour) bulundurabilir.

Veri bir nesnenin property (field, attribute) adı verilen kısımlarında saklanır.
Kod ise nesnenin method adı verilen prosedürleri içinde bulunur.

Bir nesnenin metodları, aynı nesnenin field alanına erişerek saklanan verileri okuyup değiştirebilir.

"this" nedir?
"this" adı verilen keyword ile o esnada çalışan kodun içinde bulunduğu nesnenin kendisine işaret edilir.
Yani metodlar kendilerini barındıran nesnelerin farkındadır ve ona this keywordu ile daima erişebilmektedir.

OOP'nin 4 temel kavramı

1. Encapsulation nedir?
Encapsulation, ya da kapsülleme, veri ile onu işleyen kodu paketleyerek güvenli hale getirmek demektir.

Encapsulation içerisinde iki önemli anlamı birden barındırır:
1- birbiriyle alakalı olan verileri biraraya getirerek sarmalamak (class)
2- verilere dışarıdan erişimi sınırlayıp, yapılan değişikliklerin de dışarıdaki nesneleri etkilemesini önlemek (access modifiers)

Encapsulation kavramının Java'da gerçekleştirilmiş haline bir örnek:


public class Animal {
     
    private String type;
    public String getType() {
        return type;
    }
    public void setType(String type) {
        this.type = type;
    }
         
}

Açıkta duran, herhangi bir erişim sınırlaması bulunmayan veriler bilerek ya da bilmeyerek bozulmaya müsait demektir. Encapsulation kavramı ile getirilen bu paketleme sistemi sayesinde verilere erişim sınırlaması getirilir. 
Her nesne kendi verisini kendi metodlarını kullanarak değiştirir.
Diğer nesneler sadece bu metodları çağırarak, değiştirme işlemini verinin sahibi olan nesneye emanet ederler.
Böylece istemli ya da istemsiz dış müdahaleler ile verinin bozulması önlenmiş olur.
Kısacası; her nesne kendi verisini saklamaktan ve onu değiştirmekten sorumludur.

Encapsulation'da tercih edilen paketleme yöntemi nesneleri class yani sınıflar halinde yazmaktır.
Classlar ile tanımlanan nesnelerin dilediğimiz property ve metodlarını gizleyebiliriz.
Böylece onlara erişimi engelleyerek dışarıdan gelecek müdahalelerle bozulmamasını garantilemiş oluruz.

Dışarıdan erişmek isteyen nesneler sadece erişime açtığımız metodları kullanarak sınırlı şekilde nesneye müdahale edebilirler.
Ayrıca encapsulation ile oluşan modülerlik (modularity) sayesinde bir nesnenin verilerinde olan bir değişiklik diğer nesneleri asla etkilemez.

Erişim engellemesi delinebilir mi?
Erişim sınırlaması delinebilir. Bunun için örneğin Java'da Reflection API kullanılabilir. 
Reflection API ile mesela private keywordü ile erişimi engellenmiş bir property'yi değiştirebiliyoruz.

Reflection nedir? 
Reflection, runtime sırasında bir classın property ve metodlarını okuma ve değiştirme yeteneğidir. 
Java'da reflection ile adı bilinmeyen bir classın adı bilinmeyen metod ve propertyleri incelenebilir, yeni instance yaratılabilir, metodlar invoke edilebilir.
Bir 3rd party library de bulunan bir classın private property'si reflection sayesinde değiştirilebilir.
Reflection genelde test amaçlı kullanılır (örneğin runtime esnasında mock objeler yaratmak için).

// without reflection
Foo foo = new Foo();
foo.hello();
 
// with reflection
Object foo = Class.forName("com.example.Foo").newInstance();
// Alternatively: Object foo = Foo.class.newInstance();
Method m = foo.getClass().getDeclaredMethod("hello", new Class<?>[0]);
m.invoke(foo);

2. Inheritance nedir?

https://docs.oracle.com/javase/tutorial/java/IandI/subclasses.html

Inheritance, bir classın (ya da nesnenin) diğerinin implementasyonunu aynen kullanarak onu temel almasıdır. 
Public class ve interface'ler kullanılarak temel alınan bir kodun kendinden bağımsız uzantılarını yaratmak için geliştirilmiştir.
Code reuse yani bir kez yazılan kodun tekrar kullanılabilmesini sağlayan bir yöntemdir.

Single inheritance nedir?
Bir class sadece tek bir classtan kalıtım alabilir, onun property ve metodlarını kullanabilir.
Java da single inheritance vardır.

Multiple inheritance nedir? 
Bir class birden fazla classtan kalıtım alabilir.
c++ da multiple inheritance vardır.

Kalıtım alınamayan classlar var mıdır?
Java'da final keywordu ile tanımlanan classlardan kalıtım alınamaz.
Bunun amacı genelde precompiled binaryleri olup da kaynak kodu olmayan classlardan kalıtım alınmaya çalışılmaması içindir.
Bu şekilde final keywordu ile mühürlenmiş sınıfların alt sınıfları olmadığından, referans verilen instance'ların daima kendi sınıfına ait olduğu (asla bir alt sınıfa ait olamayacağından) compile time sırasında garantilenmiş olur.
Bu sayede de kesin olarak nesnenin tipi compile time'da bilindiğinden early binding yapılması mümkün olur (late binding yaparak herhangi bir virtual metod tablosuna bakmaya gerek kalmaz).

Kalıtım ile subclasslar tarafından override edilemeyen metodlar var mıdır?
Java'da final keywordu ile mühürlenen metodlar kalıtım ile alt sınıflarca override edilemez.
Ayrıca private olarak işaretlenmiş metodlara diğer sınıflarca erişim engellendiğinden otomatik olarak alt sınıflar tarafından override edilemez.

Final keywordünü biraz açalım.
final class:
final metod:
final property:

Object classı nedir?
Java'da bütün classlar Object classından türerler. Yani bütün nesnelerin ortak atasıdır diyebiliriz.


3. Polymorphism nedir?

https://docs.oracle.com/javase/tutorial/java/IandI/polymorphism.html

Polymorphism, birden fazla tip için tek bir arayüz sağlanabilmesidir. Bir çok yöntemle sağlanabilir.

Bir metoda operator overloading uygulanırsa farklı argümanlarda farklı versiyonlarının kullanılması sağlanabilir.
Örneğin toplama metodu integer ve String tipleri için overload edilerek argümanın tipine göre uygun olan versiyonun seçilmesi sağlanabilir.

Overloadingi biraz açalım.
//TODO


Aynı şekilde Java'da Generic sınıflar kullanılarak parametrik olarak polymorphic metod ya da classlar yazılabilir. Örneğin Iterator sınıfının metodları Iterable olan her tipte nesne tarafından çağırılabilir.
Aynı şekilde Comparator sınıfı Comparable olan her tipte nesne için kullanılabilir.
List sınıfı ise her tipte nesneyi içinde barındırabilir.

Subclassing yani inheritance kullanarak da polymorphic metodlar yazılabilir.
Örneğin yürüme metoduna parametre olarak abstract bir hayvan sınıfını verdiğimizde, bu sınıftan türeyen bütün concrete alt sınıf tipleri bu metoda birer argüman olabilecektir. 
Bu metodun içerisinde de parametre olan sınıfın yürüme metodunu çağırırsak, bu durumda runtime sırasında argüman olarak verilen tip hangisi ise o classın override ederek özelleştirdiği kendi yürüme metodu çalışacaktır.

abstract class Animal {
    abstract String talk();
}
 
class Cat extends Animal {
    String talk() {
        return "Meow!";
    }
}
 
class Dog extends Animal {
    String talk() {
        return "Woof!";
    }
}
 
void letsHear(Animal a) {
    println(a.talk());
}
 
void main() {
    letsHear(new Cat());
    letsHear(new Dog());
}

Başka bir örnek:
Bicycle classından çeşitli tipler türetiyoruz.
Sonra bu tiplerin instance'larından Bicycle classı metodlarını çağırıyoruz.
JVM burada referans tipine bakarak metodu çağırmıyor. Referans verilen nesnenin kendi özel metodunu çağırıyor. Buna Virtual Method Invocation denir.


public class TestBikes {
  public static void main(String[] args){
    Bicycle bike01, bike02, bike03;

    bike01 = new Bicycle(20, 10, 1);
    bike02 = new MountainBike(20, 10, 5, "Dual");
    bike03 = new RoadBike(40, 20, 8, 23);

    bike01.printDescription();
    bike02.printDescription();
    bike03.printDescription();
  }
}

Virtual method nedir?
Bir polimorphic metodun hangi versiyonunun kullanılacağı runtime'da belirleniyorsa bu metod virtual'dır.
Java'da bütün metodlar virtual metoddur. Daima runtime sırasında late-binding (dynamic binding) yöntemiyle metodun hangi versiyonunun kullanılacağı belirlenir.

Not: c++'da bunu belirtmek için başına virtual keywordu konmalıdır. Aksi halde early (static) binding ile compile time sırasında belirlenir.



4. Abstraction nedir?
Verinin ya da kodun karmaşıklığını sistemli şekilde yönetmektir.
OOPde abstraction metodlara uygulandığında, yani  birden fazla tip aynı metoda argüman olarak verilebildiğinde, buna polymorphism adı verilir.
Tip veya sınıflara uygulandığında, yani birden fazla sınıf birbirinin yerine geçerek karmaşık ilişkiler kurabildiğinde, buna inheritance ya da delegation denir. 

Java'da abstract ve interface keywordleri ile abstraction uygulanarak davranışlar ile detayları birbirinden ayrılabilir. Böylece kullanıcılar duruma göre değişen implementasyonları dilediği gibi yazabilir.
Bir davranışın tanımlanabilmesi için detayının bilinmesine gerek yoktur.

Örnek ile açıklayalım:
java.util.Collections classında sort() metodu bulunur.
public static <T> void sort(List<T> list, Comparator<? super T> c) 
sort metodu parametre olarak bir List ve Comparator alır.
buradaki comparator, Comparator interface'ini implement eden bir nesne instance'ı olmalıdır.
Diyelim ki books adında bir ArrayList yaratarak içinde Book class instance'ları barındırıyoruz.
Kitapları sıralamak için sort metodundan yararlanmak istiyoruz.
Burada sort() metodunu kullanabilmek için ikinci parametresine vermek için custom bir comparator yazmamız gerekir.

Comparator interface'ini implement ederek bir BookComparator classı yazarız. compare metoduna da diyelim ki kitabın barkod numarasına göre sıralama işlemini yazarız.

Bu noktadan itibaren Collections.sort(books, new BookComparator()) şeklinde listeyi sıralayabiliriz.

Burada Java'daki abstraction kavramından yararlanamasaydık, yeni bir sort metodu ve içine de compare işlemini sıfırdan yazmamız, bütün bu sistemi yeniden oturtmamız gerekecekti.
Daha sonraları bir Student sınıfı yazıp not ortalamalarına göre sıralama yapmak istediğimizde, yine en baştan bu sistemi bir kez daha sıfırdan yazmamız gerekecekti...
İşte abstraction sayesinde sadece basit bir interface'i implement ederek bütün bu angaryadan, defalarca aynı davranışı (behaviour) gerçekleştiren kodun tekrarını yapmaktan kurtulmuş oluyoruz.



abstract class nedir?
abstract keywordu ile tanımlanır. Instance alınamaz fakat kalıtım alınabilir.
Alt sınıf, üst sınıfın bütün abstract metodlarına birer implementation sağlamak zorundadır. Aksi halde kendisi de abstract olarak tanımlanmak zorundadır.

abstract metod nedir?
İmplementasyonu olmadan, {} parantezleri bulunmadan, abstract keywordü ile tanımlanır.
Mutlaka abstract class ya da interface içinde olmalıdır. Concrete class içine yazılırsa bu class abstract olur.

Not: bir interface'deki metodlar default ya da static ile işaretlenmedikçe implicit olarak abstracttır.
Bu yüzden abstract keywordu yazmaya gerek yoktur.

interface nedir?
Birden fazla modülün birbiri ile etkileşimini belirli bir sisteme oturtan bir nevi kontrattır.
Bir modülün yazarının diğer modülün içeriğini bilmeden onu dilediği gibi kullanabilmesini sağlar.
Ya da modüllerin birbirlerinin içeriğini bilmeden aralarında gereken iletişimi, veri alışverişini yapabilmelerini sağlar.

Java'da interface, class'a benzeyen bir referans tipidir.
Bir referans tipinin kullanıldığı her yerde bir interface kullanılabilir.
Eğer bir referans variable'a tip olarak interface verilmişse, ona atanan değer mutlaka o interface'i implement eden bir tip olmalıdır.

Instance alınamaz. Başka sınıflarca implementasyonu yapılabilir (implements) ya da başka interface'lerce kalıtım alınabilir (extends).
Bir interface birden fazla interface'ten kalıtım alabilir, bunlar virgülle ayrılarak belirtilir.
public olarak belirtilmezse package-private olarak kalır ve sadece paket içerisinden erişilebilir.

interface keywordu ile tanımlanır ve sadece belli başlı property ve metodları barındırabilir.
Bunlar constant propertyler, abstract, default, static metodlar, ve nested tiplerdir.
interface'de constant declaration'lar yazılabilir. Bunlar otomatik olarak public static final dir. Bu yüzden bu keywordleri yazmaya gerek yoktur.
default olarak interface'de bulunan her metod signature'ı abstract olduğundan, abstract keywordu ile bunları belirtmeye gerek yoktur.
default ve static keywordleri ile de içeriği olan metodları tanımlayabiliriz.
Bu 3 türün hepsi public olduğundan metodların başına public yazmaya gerek yoktur.
abstract metodların implementasyonu yoktur.
default ve static metodların ise implementasyonu vardır.
default metodlar kalıtım ile alt interface'lere aktarılır.

Interface'in sağladığı abstraction'ın pratikte nasıl yararı olduğu konusunda bir örnek verelim. 
Örneğin bir API kullanıma açıldıktan sonra implementasyonu değişebilir. interface yani arayüzü hep aynı kaldığından kullanıcıların bundan haberi olmaz.

Abstract class ile interface karşılaştırması 

Benzer yönleri:
1) İkisinden de instance alınamaz. 
2) İkisinde de implementasyonu olan ve olmayan metodlar bulunabilir.

Farkları: 
1) Access modifiers - erişim kısıtlaması

Abstract classlarda static yada final olmayan, access modifierlar ile erişimi kısıtlanabilen concrete metodlar yazılabilir.

Interface'lerde ise bütün propertyler public static final'dir. 
Bütün metodlar da public'tir. 
Erişim kısıtlaması yapılamaz.

2) Multiple Inheritance - çoklu kalıtım

Bir class sadece tek bir abstract classtan kalıtım alabilirken, bir class birden fazla interface'i implement edebilir.

Hangisini ne zaman kullanmalı?

Abstract class: 
1) bir çok classın ortak özelliği varsa, hepsinde çoğu aynı olan kodları tekrarlamak istenmiyorsa
2) bu classların erişim kontrolü önemli ise
3) propertylerin static ya da final olmamaları, yani nesnenin özelliklerinin yine kendi metodları aracılığıyla değişebilmesi gerekiyorsa.

Interface: 
1) Birbiriyle alakası olmayan bir çok class onu implement edecekse
2) Bir veri tipinin davranışı belirtilmek isteniyorsa, fakat bu davranışı kimin uyguladığı da önemli değilse
3) Bu tipin multiple inheritance ile başka tipler tarafından kalıtım alınabilmesi isteniyorsa

Abstraction ve Encapsulation arasindaki farklar

Ortak özellikler: ikisi de veriye olan erişimin sistematik olarak kısıtlanması amacını taşır.

Farklar



Encapsulation bir nesnenin içerdiği veriyi okumak ya da yazmak için yapılan erişime kısıtlama getirilmesidir.
Böylece bir nesnenin içerdiği veri sadece kendi metodları kullanılarak, ve onlara erişebilen nesneler tarafından okunup yazılabilir.
Bir nesne class sınırları içerisine hapsedilip access modifierlar ile property ve metodlarının erişimleri tanımlandığında, enkapsüle edilmiş olur.

Abstraction ise bir uygulama ile o uygulamanın kullanılabilmesi için gereken arayüzün birbirinden ayrılmasıdır.
Implementasyon değişecek olsa bile, girdisi-çıktısının kabaca belirlendiği, üzerinde anlaşılmış olan metod grubunun kısa bir özetidir.
Bir public interface yazarak implementasyon detayları hakkında hiçbir sınırlama getirmeden, kullanıcıya ana hatlarıyla bilmesi gerekenleri signature'lar aracılığıyla sağlarız.
Kullanıcı sadece metod signature'larını gördüğünden, her metodun input parametreleri ve return tiplerini öğrenir. 
Bu bilgiye dayanarak implementasyonunu yazar ve bu uygulama interface ile uyumlu olmuş olur.
Artık bu implementasyon, orijinal interface'in kullanıldığı her yerde kullanılabilir.

Hiç yorum yok:

Yorum Gönder