Yazılımın ilk 250 günü - Part 7
61. Gün
Bugün sizin bize yorumlamamız için gönderdiğiniz javascript kodlarını hayranlıkla okudum. O kadar iyi düşünülmüş ki ben 5 gün düşünsem yine de yapamazdım gibi geldi. 2 ayda okur yazarlık edinildi fakat iyi bir yazar,edebiyatçı,şair olmak için çok uzun bir yol var bugün yine anladım. İngilizce için söylenen “anlıyorum ama konuşamıyorum” ifadesini çok iyi hissettim bugün. Evet yazılan kodun amacını, kullanılan terimlerin anlamını biliyorum ya da kısa bir araştırmayla hemen bulabiliyorum ama iş yapmaya gelince kıvrak ve zeki bir yaklaşımı hâlâ sergileyemiyorum. Biraz da tecrübe ile gelişir diye umuyorum. Çok inanıyorum olacağına, çünkü çok seviyorum. Çok fazla umut, inanç,sevgi gibi pozitif içerikli kelimeler kullandım sanırım. Sebebi ise mesai bitiminde DOM ve event handlers konusuna tekrar bakarken dinlediğim Daft Punk şarkısı sanırım.Günü o şarkı sözü ile bitirmek istiyorum:
“Work it harder, make it betterDo it faster, makes us stronger
More than ever, hour after hour
Work is never over”
62. Gün
Günün başlangıcında satranç oyununun OOP yöntemi ile nasıl yazılacağı üstüne bir kaynak buldum. Java kullanarak yazılmış kodu PHP’ye döndürüp, sonrasında HTML ve CSS kullanarak bunu bir web sayfasında göstermek istedim. OOP için pratik olur diye düşündüm fakat uzun sürecek gibiydi ve boş durmamak için onunla uğraştım. Sonrasında sizin gönderdiğiniz kodu inceledim. Kullanılan yapıları anlamaya çalıştım. Neden ve nerede kullanılabileceği üstünde çok düşündüm. Aklıma çok bir şey gelmedi amacı konusunda ama en azından kodu okudum. Yorumlamaya çalıştım. Tüm gün bununla geçti diyebilirim. ArrayObject yapısı ile ilk kez karşılaştım(karşılaşmışım daha önce ama onun ne demek olduğunu bilmiyormuşum) ve direkt dokümandan anlamaya çalıştım. Bu kodu anlamak ya da anlamaya çalışmak zorladı. Ve zorlanmadan da gelişim olmayacağı için çabaladım elimden geldiğince. Bugünlük bu kadardı.63. Gün
Dün sadece inheritance, abstract class , ve interface konularını tekrar ettim. Oturmayan yerler kaldıysa oturtmaya çalıştım. Teorik olarak oturmayan bir yer keşfedemedim, üstüne bir şeyler katmaya çalıştım. Bol bol okumalar yaptım üstüne. Interface, senin ürettiğin sistemde başka developer’lar çalışacağı zaman üretecekleri class’larda bulundurmaları zorunlu metotları tanımlamak için var. Abstract class ise senin sisteminde çalışacak developer’lara birtakım abstract method’ları kullandırtmak ve childs class’larını üretirlerken bazı base method’ları onlara sağlamak için kullanılır, kullanılmalıdır. İkisinin de artıları ve eksileri bulunmaktadır. Nasıl bir yapı kurulacağına göre tercih edilme sebepleri de değişkenlik gösterecektir. Kesinlikle interface ya da kesinlikle abstract kullanmalısın gibi bir durum söz konusu değil. Projeye, systemin gerekliliklerine göre değişebilir. Bildiğimiz gibi interface’I implement edecek her class, interface’de bulunan her method’u kullanmak zorundadır. Birden fazla kez class’lar tarafından implement edilebilirler. Tamamen farklı ve alakasız sınıflar tarafından da kullanılabilirler. Inheritance bu işin neresinde peki denilirse cevap şu olabilir: Interface yapısı sadece fonksiyonel bir inheritance sağlar.Inheritance konusunda çokça duyduğum bir bakış açısı var:
Abstract class bir “is a” ilişkisini concrete class’lar ile kurar. Bu sadece abstract class için değil aynı zamanda herhangi bir “base class-child class” ilişkisi için de geçerlidir. Interface ise “has a” capability’sini(yeteneğini) class’lar için sağlar.
Interface kısaca bir contract, bir sözleşmedir. Interface’I yazan kişi der ki, “Dostum, ben şey’leri bu şekilde kabul ederim”. Kullanan kişi de der ki, “Tamamdır, yazacağım class’lar bu şekilde olacak size temin ederim”. Yani arayüz boş bir kabuk gibidir. Sadece method’ların ne olacağını belirtir. İçi dolu değildir. Kendi başlarına bir şey yapamazlar. Onlar sadece bir pattern’dir. Interface’ler çok az CPU tüketirler. Çünkü onlar bir class değildir. Bunun önem kazandığı noktada, gömülü yazılım gibi konularda dikkate alınabilirler bu açıdan.
Abstract class’lar ise interface’in aksine bir class’tır. Interface lere benzemektedirler elbette ama birkaç fark bulunmaktadır. Örneğin Onlara bir davranış tanımlayabilirsin. Yani method’ların içi dolu olabilir ya da içinde değişken-property tanımlaman mümkün. Abstract class’ta daha çok “Bu class’lar şu şekilde görünmeli, ve onların ortak yanları da olabilir. Ayrıca aynı yöntemi farklı şekilde kullanabilirler. Ben sana kullanacakları yöntemi söyleyeyim. Onu sen istediğin şekilde doldur.” düşüncesi hâkimdir.
64. Gün
Bugün tekrar niteliğindeydi. Daha önce görmüş olduğumuz konulara tekrar baktım. Nesneler arası ilişkileri tekrar ettim. Abstaction kavramını, polymorphism’I, inheritance’I ve OOP ile alakalı diğer bir birçok konuyu. OOP tarafında bugün baktığım konularda eksik olduğum noktaları size ve google’a sordum. Aklımda kalan sorulara cevap bulabildim. Daha ayrıntılı olarak ne anladığımı yarın genel manada yazacağım. Gün içinde anladığım ve anlamadığım birçok ayrıntıyı not aldım. Yarın design patterns konularına kaldığım yerden devam edeceğim.65. Gün
Bugün ve dün aldığım notların bir kısmını bugün raporuma eklemek istiyorum. Özet niteliğinde yazmak istediklerim:- OOP’yi kullanma amaçlarından bazıları: Karmaşıklığı azalt, bakım maliyetini düşür, modülariteyi arttır, nesneler arası hiyerarşiyi kur.
- OOP dört temel ilke üzerine kurulu: Encapsulation, abstraction, inheritance, polymorphism.
-
Nesneler arası ilişkiler: Inheritance, implementation, association, dependency, composition, aggregation. Bu yapıların basitçe kodlanması:
- Inheritance: is+a (A is a B),
- Implementation: should + do (A should do B),
- Dependency: references (A references B),
- Composition: has + a, wholepart, ownership,
- Aggregation: has + a, wholepart.
- Dependency
- Hem aggregation hem de composition için bir örnek
- Bu konuda son bir örnek
- Association: Bir obje ile ikişkim var. “Foo uses Bar”.
- Composition: Bir objeye sahibim ve onun lifetime’I ile yükümlüyüm. “When Foo dies, so does Bar”.
- Aggregation: Başka birinden ödünç aldığım bir objeye sahibim. “When Foo dies, Bar may live on”.
- Farklı bir konuda aldığım farklı bir örnek. Abstraction’a dair
- SOLID
- Single Responsibility Principle
Her sınıf, method, fonksiyon tek bir sorumluluğa sahip olmalıdır. Eğer sen bir sınıfa birden fazla sorumluluk verirsen, ilerleyen süreçte kodda bir değişikliğe gitmen dahilinde sorunlar çıkacaktır. Bu da maliyeti arttıracaktır. Clean code’da şöyle denir: “There should never be more than one reason for a class to change”. - Open Closed Principle
“Software entities should be open for extension, but closed for modification”.Yani kullanıcılara mevcut kodu değiştirmeksizin yeni fonksiyoneliteler eklemeyi sağlamalısın.
- Liskov Substitute Principle
Alt sınıflar, üst sınıflardan türediği için onların davranışlarını devralırlar. Eğer üst sınıflara ait davranışları gerçekleştirmiyorlarsa davranışı yapan metodu muhtemelen boş bırakır ya da bir hata fırlatırız. Fakat bu işlemler kod kirliliğine ve geçersiz kod kalabalığına neden olmaktadır. Bunların yanı sıra projeye daha sonradan dahil olacak geliştiriciler için de sorun oluşturmaktadır. Geliştirici systemin sağlıklı yürüdüğünü düşünerek gerçekleştirilmeyen bir davranışı kullanmaya çalışabilir.
- Interface Segregation Principle
Bir sınıf, birden fazla arayüzü uygulaması özelliğiyle de birlikte bu prensip, bu tür durumlarda arayüzlerin ayrılmasını ve ihtiyaç hâlinde olanların kullanmasını söylemektedir.
“Clients should not be forced to depend upon interfaces that they do not use”.
- Dependency Inversion Principle
Design Patterns
“Her desen, çevremizde tekrar tekrar ortaya çıkan bir sorunu açıklar ve daha sonra bu soruna çözümün uygulanmasını, bu çözümü iki kez aynı şekilde yapmadan milyonlarca kez kullanabileceğiniz şekilde tanımlar.” - Christopher Alexander
“Design patterns are solutions to recurring problems; guidelines on how to tackle certain problems”.
“Aklında tut! Design pattern’ler problemlere çözümdür. Problem bulmaya yarayan bir çözüm değildir. Eğer doğru yerde doğru şekilde kullanırsan onlar birer kurtarıcıdır, aksi hâlde kodunda korkunç sonuçlara neden olabilir.”
Creational Design Patterns
Bir nesnenin ya da bir grubun esnek ve tekrar kullanılabilir biçimde nasıl oluşturulacağı ile ilgilenir. Çeşitleri: Singleton, Simple factory, factory method, abstract factory, builder, prototype.- Singleton
Sistem boyunca aksiyonları koordine etmek için sadece ama sadece bir tane object isteniyorsa kullanılması faydalıdır.
Conctructor private olmalıdır. Bunun amacı, nesne oluşumunu tek bir yerden sağlamak; istemcinin, “new” anahtar kelimesini kullanmasını engellemektir. Statik bir değişken olmalıdır, referansı tutmak için. Tutulan referansa erişmek için bir metod olmalıdır, getInstance().
Multithread uygulamalarda aynı anda birden fazla thread nesne oluşturabilir farklı farklı. O an bulunan dilde bunu önleyen yapılar var (lock, synchronized gibi).
- Singleton’ın dezavantajları
- En büyük dezavantajı, varoluş sebeplerinden biri: global olması. Ayrıca anti-pattern davranış sergilemesi. Global instance olarak kullanılması neden kötü peki? Uygulamanın dependency’lerini bir interface ile göstermek yerine kodunda saklarsın. Bir şeyi global yapmak code smell’e sebep olur. (Code smell’I henüz çok araştırmadım).
- Single responsibility prensibine tersitr. Çünkü kendi oluşturduklarını ve yaşam döngülerini control ederler.
- Kodu kalıtımsal olarak sıkıca bağlı yaparlar (tightly coupled).
- Tightly coupling’e çözüm olarak interface kullanılabileceği, test sorunlarında ise singleton class’ının yapısının değiştirilip kullanılabileceği kimi örneklerde söylenmiş. Bu şekilde kullanımla singleton’ın sorun çıkarabilecek kısımları çözülebilmektedir. Ama kullanırken(eğer doğru yerde doğru şekilde kullanmayı çok iyi bilmiyorsan) çok dikkatli olmak gerekiyor.
- Uygulamanın lifetime’I boyunca statelerini korurlar. Bu da yine unit test için kötü bir şeydir. Çünkü her test diğerinden bağımsız olmalıdır. Yani application’ın davranışına etki edemeyecek şekilde kullanılmalıdır.
Bu konudaki özlü söz:
Creational Design patterns’lara devam ederken kısa bir ara: Prefer composition over inheritance. Neden composition’I inheritance’a tercih etmeliyiz fırsat buldukça?“When you think you need a global, you’re probably making a terrible design misktake”.
- Runtime sürecinde superclass’lardan miras alınan implementation’I değiştiremezsin (Çünkü inheritance compiled time’da tanımlanır).
- Inheritance, sublass’ın parent class’ın implementation’ını detaylandırmasını ortaya çıkarır. Bu yüzden inheritance’ın encapsulation’I kırdığı belirtilir.
- Inheritance tarafından sağlanan tight coupling, subclass’ın implementation’ını parent’ın implementation’ına sıkı sıkıya bağlar. Yani parent’ın implementation’ındaki her değişim subclass’I değişime itecek, zorlayacaktır.
- Subclass’ın aşırı kullanımı, kalıtım yığınını çok derin ve kafa karıştırıcı yapabilir.
- Diğer yandan, object’ler diğer object’lere referans kazandırırken, object composition’I runtime’da tanımlanır. Bu durumda bu objeler, diğer object’lerin protected data’sına ulaşamayacak (no encapsulation break) ve her birinin interface’ine saygı duyacaktır.
- A Bar is a Foo ( Inheritance “is +a” ilişkisini kuruyorsa ve “has + a” ile alakası yoksa),
- Bar, Foo’nun yapabildiği her şeyi yapabilir ( Base class’lardaki kodu tekrar kullanabiliyorsan),
- Base class’I değiştirerek türetilen class’larda global değişiklik yapmak istiyorsan.
- Simple Factory
- Factory Method
- Abstract Factory
- Builder
66. Gün
- Simple Factory
In plain words:
Kısaca başka object’leri yaratmak için kullanılan bir object’dir.“Simple factory simply generates an instance for client without exposing any instantiation logic to the client”.
Ne zaman kullanılır? Object oluşturmak birkaç basit işlemle olmuyorsa ve biraz mantık içeriyorsa oluşturma aşamasında, her yerde kodu tekrar etmek yerine bir “dedicated factory” yazmak mantıklı olur.
- Factory Method
Örneğin logistic app’imiz var diyelim. Bu app ile sadece kara lojistiğini destekliyoruz. Eğer biz deniz taşımacılığını app’imize dahil etmek istersek bir sorun var! Kodumuzda varolan class’lar birbirleriyle tightly coupled vaziyette. Böyle bir durumu çözmek için ne yapabilirsin? Factory method burada kullanılıyor ve çözüm sağlayabiliyor.
Ne zaman kullanılmalıdır? Kodun çalışması için boject’lerin dependency’lerini ve exact type’larını önceden bilmiyorsan kullanabilirsin. Library ve framework’ümüzün dahili component’lerini extend edecek bir yol ile kullanıcılara sağlamak için de kullanılabilir. Varolan object2leri her zaman tekrar inşa etmek yerine onları tekrar kullanarak sistem kaynaklarını korumak için de kullanılabilir.
Ek olarak, factory method ürün üretimi ile ürünün asıl kullanıldığı yeri ayırır. Bu da ürün üretimi tarafını kodun diğer kalan yerlerinden bağımsız olarak genişletebilmemizi sağlar. Örneğin app’ine yeni bir ürün eklemek için, sadece yeni bir creator subclass’ı yaratıp factory method’u override etmen yeterli.
Artıları: Creator ve concrete product’lar arasındaki tight coupling önlenir. SRP ve OCP’ye tamamen uymaktadır doğru yazıldığı takdirde.
Bu konuya bir örnek daha verelim. Diyelim ki sitemize giriş yapmak ya da üye olmak için sosyal medya hesaplarını kullanarak oluşturmak/girmek isteyen kullanıcı Facebook ile giriş yapabiliyor. Bizim sistemimiz buna tamamen uyumlu olsun. Ama eğer kullanıcılar artık linkedin hesabı ile de girmek isterse ve gelecekte başka eklemeler yapmak istersek burada factory method ile bu imkanı sağlarız. Kodumuzu daha flexible hale getiririz.
Factory method içeriklerinin çalışma prensipleri:
- Creator class’ımız factory method’u tanımlar ve ürün class’ının objesini döner. Creator’ın subclass’ları genelde bu metodun uygulanmasını sağlar.
- Creator isterse factory methodun default implementation’ını da sağlayabilir.
- Creator’ın asıl yükümlülüğü product yaratmak değildir. Method İle geri dönen product objesi ile alakalı temel business logic’ini içermektedir. Subclass’lar dolaylı olarak method üstüne overriding yaparak business logic’i değiştirebilir ve başka bir product tipini döndürür.
- Concrete creator’lar product tipini değiştirmek için factory method’un üzerine yazarlar.
- Product interface’leri tüm concrete product’ların uyması gereken operasyonları tanımlar.
- Concrete product’lar, product interface’inin implementation’larını sağlar.
- Tüm bu yazdıklarım sonradan okuyunca çok manasız görünüyor çünkü örnek bir kod olmadan bir anlam taşımıyor gibi. O yüzden bu kadar detaylı kısımları atlayacağım. Fakat abstract factory ve factory method şu üç günde en zorlandığım kısımlar olduğu için biraz fazla not aldım. O yüzden önemli görülen yerleri buraya yazmaya çalışacağım.
- Abstract Factory
Birden fazla ürün ailesi ile çalışmak zorunda kaldığımız durumlarda, istemciyi bu yapılardan soyutlamak amacıyla Abstract Factory doğru bir yaklaşım olacaktır.
Simple factory’deki kapı örneğimize dönelim. İhtiyacına göre marangozdan tahta, demirciden demir kapı, plastikçiden PVC kapı alırsın. Ayrıca bunların her biri için de ayrı ayrı takacak usta bulman lazım. Çünkü hepsinin ustası farklı. Bir tahta kapı oluşturmak için tahta ve ustası gerekir. Diğerleri için de aynı şekilde geçerlidir. Kapısı oluşturmak için iki farklı objeyi aynı noktada buluşturuken Abstract Factory’den yararlanırız.
Ne zaman kullanırız? İlgili ürünün farklı aileleri ile kodun çalışmaya ihtiyaç duyarsa, ama bu product’ların concrete class’larına bağlı olmasını istemezsen (Öncesinde bilinmeyebilirler ve future extensibility’ye izin vermek istersin) kullanabilirsin.
Farklı bir örnek verelim: Bir web sayfasının farklı elementleri için değişik tipte templates’lerin yaratılması için bir altyapı sağlayabilirsin. Bir web uygulaması aynı anda farklı rendering engines’leri destekleyebilir ancak onun class’ları rendering engine’in concrete class’larından bağımsızsa. Bu yüzden, app’in object’leri ile yalnızca onların abstract interface’lerini kullanarak iletişim kurmak zorundasın. Senin kodun direkt template object’lerini yaratmamalı, ama onların yaratılması için özel factory object’leri atamalıdır. Son olarak, kodun factory object’lerine bağımlı olmamalıdır, ama yerine abstract factory interface aracılığı ile onlarla çalışmalıdır. Sonuç olarak , app’e, rendering engines’lerden birine denk gelen factory object’sini sağlayabileceksin. App’te yaratılan her template, factory tarafından yaratılacak ve ve onların tipi factory’nin tipiyle eşleşecek. Eğer sen rendering engine’I değiştirmek istersen, mevcut kodu bozmadan client koduna yeni bir factory geçebileceksiniz.
Farklı kaynaklardan ek olarak şunlar söylenebilir:
Particular dependency yaratmak için runtime değerlerine ihtiyaç duyduğun her yerde Abstract factory’yi kullanabilirsin.
Abstract factory’nin gerçek hayat örneklerini araştırdıkça Dependency Injection konularında sıkça kullanıldığını gördüm. Esasında kullanılan yerlerden bazılarına örnek olarak:“ The abstract factory pattern provides a way to encapsulate a group of indivual factories that have a common theme without specifying their concrete classes.” - Wikipedia
- You need to supply one or more parameters only known at runtime before you can resolve a dependency,
- The lifetime of the dependency is conceptually shorter than the lifetime of the consumer.
Factory method ve Abstract Method arasındaki farklar:
Abstract factory, yaratılması gereken object’ler için abstract method’ları içeren base class’ları yaratır. Base class’tan türeyen her factory class, her object tipinin implementation’ını kendi yaratabilir.
Factory method, bir class’ta object’ler yaratmak için kullanılan basit bir method’dur.
Özetle; Factory method’u içeren class’ın asıl amacı obje yaratmak değildir. Abstract Factory ise sadece object yaratmak için kullanılmalıdır. Diğer bir fark ise; abstract factory composition ile implement edilir, ama factory method inheritance ile. Kavranması gereken en önemli nokta abstract factory client’a enjekte edilir. Bu yüzden composition var deriz.
Not: Nesneleri yaratırken LSP’ye ters bir şey yapmak kolay olduğu için factory method’ları kullanırken dikkatli olmalısın.
- Builder
Burger King'e gittik diyelim. İkili menu istedik ve onlarda getirdi. Bu basitçe simple factory’ye benzetilebilir. Ama creation logic’inin daha fazla adım içerme ihtimali de vardır. Mesela “Burger’ın nasıl olsun? Et az pişmiş mi? Hangi sosları eklememizi istersiniz? İçinde turşu ister misiniz? “ gibi. İşte burada builder pattern bizi kurtarmaya gelir.
In plain words:
Constructor’ımızın için birçok parametre varsa bunu karışma ihtimali çok fazladır. Parametre sayısı daha da artarsa içeriğini düzenlemek daha da zorlaşır. Bu durum telescoping constructor anti-pattern diye geçer literatürde. Buna tek alternatif ise builder pattern’i uygulamaktır.“It’s a design pattern with the intentions of finding a solution to the telescoping constructor anti-pattern”. - Wikipedia
Factory pattern ile arasındaki ana farklılık: “üretim tek adımlı bir işlemden geçiyorsa factory pattern, üretim birçok aşamadan oluşuyorsa builder method kullanılır.”.
Eğer telescoping constructor pattern’ine karşı olarak; instance’ı cilent’ta oluşturup her method’u tek tek çağırırsan,birkaç çağrı üzerine object yaratıldığı için concstruction’ın herhangi bir anında inconsistent (tutarsız) bir durum oluşabilir. Bu da thread safety’yi sağlamak için ekstra çabaya neden olabilir. Bu yüzden en iyi alternatif builder pattern’dir.“The builder pattern is a good choice when designing classes whose constructors or static factories would have more than a handful of parameters”. - Joschua Bloch
Faydaları:
- Parametre değerlerinin hepsi tek bir lokasyonda bulunur.
- Kodun daha kolay yazılıp, okunmasını, ve anlaşılmasını sağlar.
- Parametreleri control etmek için build method modifiye edilebilir ve eğer geçersiz bir parametre değeri verildiyse “IllegalStateException” hatası throw edilebilir.
- Flexible’dır. Gelecekte parametre eklemek çok daha kolaydır.
- Prototype
PHP’de klonlama işlemi direkt “__clone” magic method’u ile yapılabilir. Ya da direkt “clone” ile de yapabilirsin.
Ne zaman kullanmalı? Varolan bir objenin bir benzeri gerekliyse veya cloning’e kıyasla creation daha masraflı ise kullanabilirsin. Maliyetleri üretimi olan nesneleri az maliyetle üretmek için (Maliyetten kasıt parametreli constructor vb. olabilir). Shallow copy ve deep copy olarak iki türü var. Shallow copy ile nesnelerin bellekteki adresleri kopyalanır. Yüzeysel bir işlem olduğu için yeni bir nesne üretilmez. Deep copy ise bizim istediğimiz şekildedir. Birebir kopyalayıp, bu kopya ile asıl nesne farklı referanslar ile işaretlenebilmektedir.
Amaç: “intends to do is reduce the creation time of an object”.
Structural Design Patterns
Nesnelerin birbirleriylse nasıl birleşecekleri üzerinde durur.- Adapter
In plain words:
Bir örnek verelim gerçek hayattan: Şimdi bir sistemde external DVR’lar ile ilgili bir arayüze ihtiyaç duyulduğunu düşünelim. Her DVR üreticisi aletlerinin bizim tarafımızdan kontrol edilebilmesi için kod yazmamıza izin veren bir kütüphane sağlar. Bu kütüphaneye SDK diyelim. Her SDK, DVR’ların en temel fonskiyonalitilerini sağlayan arayüzlere sahip olsa da onların hepsi aynı benzerlikte değildir. Yani SDK’dan SDK’ya fark gösterebilmektedir. Bizim yazacağımız yazılım ise tüm DVR’lar ile etkileşimde olamlı. Her farklı SDK için ayrı bir switch case durumu açmak yerine, ortak bir arayüz yaratıp, tüm sistem kodumuzu bu arayüz vasıtası ile geliştirebiliriz. Her farklı SDK için farklı bir adapter yazılıp bizim interface’imizi implement eder. Bu şekilde kodumuzu daha basit yapabiliriz, Adapter pattern sayesinde.“Adapter pattern allows the interface of an existing class to be used as another interface. Often used to make existing classes work with others without modifying their source code”.
Daha basitçe anlaşılacak örnek ise: JSON işlemleri için sistemimiz JsonSerializer arayüzünü kullanıyor. Ama sonrasındaki süreçte 3. Parti bir şey ekleyip farklı bir arayüz kullanmak istiyoruz diyelim. Bizim sistemimiz JsonSerializer üstünden yürüdüğü için adapter pattern’I kullanarak JsonSerializer gibi görünerek 3. Parti yazılımının çalışmasını sağlıyoruz.
- Bridge
Örnek: Farklı sayfaları olan bir websitemiz olduğunu düşünelim. Kullanıcıların sayfaların theme’ini (dark mode gibi) değiştirebildiğini düşünelim. Ne yapardın? Her sayfanın tek tek kopyasını oluşturup hepsi için theme yaratmak mı? Ya da sadece kullanıcının tercihine göre yüklenecek ayrı bir theme yaratmak mı? Bridge pattern 2.’yi yapmamıza izin verir.
In plain words:
“Bridge pattern, ‘prefer composition over inheritance üzerinedir. Implementation detayları,ana hiyerarşiden uzaklaştırılıp başka bir objenin ayrı bir hiyerarşisine itilir”.“Decouple an abstraction from its implementation so that the two can vary independently”. - Wikipedia
Saat geç olduğu için yarın kaldığım yerden devam edeceğim. Direkt defterden yazığım için bazı yerler haddinden fazla uzamış ve karışık olmuş olabilir. Yazarken farkındaydım. Ama şu ana kadar en oturmayan ya da karşıma çıksa zorlanacağım dediğim yer Abstract Factory oldu. Şimdiye kadar zorlandığım birçok yer sonradan tekrar ve tekrar üstüne gidince oturmuştu. Onun da oturacağına inanıyorum.“Adapter makes things work after they’re designed; Bridge makes them work before they are”. - GOF (page 219)
67. Gün
- Composite
In plain words:
"Composite patttern, client'ın aynı tavırla bireysel nesnelere davranmasına izin verir.""Farklı nesneleri gruplayıp, bir nesnenin tek bir instance'ı şeklinde davranılmasını tanımlar." - Wikipedia
Amaç: Part-whole hiyerarşini temsil etmek için nesneleri ağaç yapısına "compose" eder. Composite pattern'i uygulamak client'ın indivual nesnelere ve compositon'lara homojen şekilde davranmasına izin verir.
Örneklendirelim: Her organizasyon, her şirket çalışanlara sahiptir. Çalışanların her biri aynı özelliklere sahiptir. Maaşa sahiptirler. Sorumlulukları vardır. Birine rapor vermeleri gerekebilir yada gerekmeyebilir. Astları bulunabilir ya da bulunmayabilir. Bunları aynı collection'a alıp istersek maaşlarını toplayabilir ya da hepsi için başka method'larda uygulayabiliriz.
"Use the composite pattern when You want to represent represent part-whole relationship; You want clients to be able to ignore th difference between compositions of objects and indivual objects. Clients will treat all objects in the composite structure uniformly." - GOF
- Decorator
"Aynı class'tan türeyen diğer objelerin davranışını etkilemeden, statik veya dinamik olarak, bir tek nesneye davranış eklemeye izin verir. Single responsibility principle'a bağlı kalmak için kullanışlıdır." - Wikipedia
- Facade
In plain words:
Design Pattern tekrar eden bir problemi çözmenin ortak bir yolunu sağlar. Design Pattern'lerdeki class'lar sadece normal birer class'tır. Önemli olan onların nasıl yapısal olarak kurulduğu ve bir problemi çözmek için en iyi şekilde nasıl beraber çalıştıklarıdır."Facade pattern provides a simplified interface to a complex system".
Facade design patterns karmaşık bir sistemin arayüzünü basitleştirir. Complex bir sistemin subsystemlerini oluşturan class'ların tümünden oluşur. Facade, kullanıcıyı sistemin karışık detaylarından korur ve kullanımı kolay basitleştirilmiş bir görünüm sağlar. Ayrıca sistemi kullanan kodu alt sistemlerin ayrıntılarından ayırarak sistemi daha sonra değiştirmeyi kolaylaştırır.
Daha önce de yazmıştım ama tekrar etmekte kendim için fayda var. Tasarım kalıplarını öğrenirken önemli olan, verilen problemimize hangi kalıbın uyduğunu fark edebilmek ve sonra onu uygun şekilde kullanabilmektir. Bir pattern'i yanlış biçimde kullanmak veya bir pattern'i bildiğin için problemini sadece onunla çözmeyi denemek büyük bir sorundur. Bu tuzaklara düşmeden design pattern'ları öğrenmekte fayda var.
Facade için özetle şunu diyebiliriz: amaç sistemi yeni bir altyapıya sokmak değil, alt sınıflardaki karmaşıklığı soyutlayarak pratiklik sunmaktır.
- Flyweight
In plain words:
"It is used to minimize memory usage or computational expenses by sharing as much as possible with similar objects."
"Flyweight, diğer benzer object'ler ile mümkün olduğu kadar veri paylaşarak hafıza kullanımını minimize etmeye çalışan bir object'dir. Basit bir tekrarlanan temsilin kabul edilemez miktarda bellek kullanması durumunda, nesneleri çok sayıda kullanmanın bir yoludur." - WikipediaGereksiz nesne initialize etmeyi bırakıp hafızada daha az yer kaplamak amaçtır dediğimiz üzere. GoF'ta bir object'nin iki durumundan bahsedilir:
"Esas Durum(Intrinsic State): Flyweight'te depolanır. Flyweight içeriğinden bağımsız bilgilerden oluşur. Bu da onu paylaşılabilir kılar.Eğreti Durum(Extrinic State): Flyweight'in içeriği ile değişebilir ve ona bağlıdır. Bu yüzden paylaşılabilir değildir. Client object'leri ihtiyaç olduğu durumda eğreti durumu flyweight'e geçirmekle yükümlüdür." ??Bu cümleyi pek anlamadım.??
- Proxy
Çoğu PHP uygulamasında pek kullanılmasa da yine de kullanıldığı bazı alanlar var: caching, logging, access control, delayed initialization gibi. Örneğin downloader da bu konuya örnek verilebilir. Proxy sayesinde caching kullanarak downloader'ın performansı arttırılabilir. Mesela bir dosyayı indirdin diyelim. Sonrasında tekrar indireceksin aynı ürünü. Birinci indirmende bu veriyi caching ile direkt sana vermek hem zamandan hem de trafikten tasarruf sağlar.
Gerçek dünyadan bir örnek olarak da kredi kartı verilebilir. Banka hesabımız için kredi kartı bir proxy'dir. Nakit para yerine kullanılabilir ve gerektiğinde nakit parayı çekmemize yarar. Bu da tam olarak proxy'nin yaptığı şeydir:
"Control and manage access to the object they are protecting".
"En genel haliyle proxy, başka bir şeye arayüz olarak işlev gören bir sınıftır. Proxy, sahne arkasında gerçek hizmet veren nesneye erişmek için istemci tarafından çağrılan bir sarmalayıcı (wrapper) veya aracı (agent) nesnedir. Proxy kullanımı, gerçek nesneye yönlendirme olabilir veya ek mantık sağlayabilir. Proxy'de, örneğin gerçek nesne üzerindeki işlemler kaynak yoğun olduğunda önbelleğe alma veya gerçek nesne üzerindeki işlemler çağrılmadan önce ön koşulların kontrol edilmesi gibi ekstra işlevsellik sağlanabilir." - Wikipedia
Structural design pattern'ları bir sonraki konuya geçmeden önce özetleyelim. Aralarındaki farkları yazalım. Çünkü hepsi birbirine yapısal olarak benzemekte ve kafamızın karışma ihtimali çok fazla. Proxy, decorator, adapter, ve bridge; hepsi bir sınıfı "wrapping" (saramalamak) üstüne varyasyonlardır. Ama kullanımları farklıdır.
Proxy: Bir nesneyi lazy-instantiate etmek istersen, veya remote bir service'i çağırdığını gizlemek için, veya nesneye erişimi kontrol etmek istersen kullanılabilir.
Decorator: "Smart Proxy" olarak adlandırılır. Bir nesnenin türünü genişletmeden, ona yeni bir işlevsellik eklemek istersen kullanılabilir. Bu, runtime'da yapmanızı sağlar.
Adapter: Bir abstract arayüzün olduğunda ve farklı bir arayüze ama çok benzer bir işleve sahip başka bir objenin arayüzü ile eşleştirmek istersen kullanabilirsin.
Bridge: Adapter'a çok benzerdir ama sen hem abstract interface'i hem de temeldeki implementation'ı tanımladığında onu kullanırsın. Örneğin, bazı 3. parti kodlara adapte olmuyorsun ve tüm kodun designer'ı da sahibi de sensin. Ve sadece bazı farklı implementation'ları değiştirmek istiyorsun.
Facade: Bir veya birden fazla sınıfın alt sınıflarına yönelik oluşturulan bir high-level interface'dir. Düşünelim ki birden çok nesnenin oluşmasını gerektiren kompleks bir yapıya sahipsin. Bu nesneler kümesinde değişimler yapmak kafa karıştırıcı olabilir çünkü hangi nesnenin senin çağırmak istediğin metoda sahip olduğunu her zaman bilmeyebilirsin. Nesnelerin koleksiyonuna yapabileceğin kompleks işlemler için high-level methodlara sahip bir facade yazma zamanı gelir!
Behavioral Design Patterns
Nesneler arası ortak haberleşmeyi efektif ve esnek bir yapıya getirmemizi sağlar.- Chain of Responsibility
In plain words:
"Nesne zinciri üretmeye yardım eder. Uygun handler'ı buluncaya dek istek bir nesneden yerden başlar ve tek tek nesneleri gezerek devam eder."3 ödeme şeklim var diyelim. Birincisi banka hesabım, ikincisi Paypal hesabım, üçüncüsü de bitcoin olsun diyelim. Bankada 100 liram var, Paypal hesabımda 200 lira var, bitcoin cüzdanımda da 300 liram var toplamda diyelim. Şimdi ben 259 liralık alışveriş yapacağım. Diyorum ki, "sevgili banka hesabım sende bi 259 almam lazım". Ama görüyoruz ki orada o kadar yok. Banka hesabım da diyor ki "Ben seni Paypal hesabına yönlendireyim. Bende o kadar bozukluk yok" dedi. Paypal hesabında da o kadar para olmayınca, bitcoin cüzdanına yönlendirdi ve sonunda orada para olduğu için işlemin tamamlandı. Burada yapılan şey nesnelerin davranışı ile ilgiliydi. Yükümlülük zinciri gibi bir ismi var, ve tabii ki nesneler için. En azından biz OOP üstüne ilerlediğimiz için.
- Command
In plain words:
"Eylemleri nesnelerde kapsüllememizi (encapsulation) sağlar. Bu kalıbın arkasındaki ana fikir, istemciyi alıcıdan ayırmanın yollarını sağlamaktır."
"Bir eylemi gerçekleştirmek veya sonraki bir zamanda bir olayı tetiklemek için gereken tüm bilgileri kapsüllemek için bir nesnenin kullanıldığı davranışsal bir tasarım kalıbıdır. Bu bilgiler, yöntem adını, yöntemin sahibi olan nesneyi ve yöntem parametreleri için değerleri içerir." - WikipediaÖrnek verelim. Bir restaurantta sipariş verdiğini düşün. Sen (Client), garsona (Invoker) iskender (Command) getirmesini söylersin. Ve garson senin talebini şefe (Receiver) iletir, ki şef iskenderi nasıl pişirileceğine dair bilgisi olan insandır). Diğer bir örnek ise: Sen (Client), uzaktan kumandayı (Invoker) kullanarak televeziyonu (Receiver) açarsın(Command).
Bugün behavioral design pattern'lara geçiş yaptım. Yarın tamamen bitirip cumartesi günü hepsini tekrar edeceğim.
68. Gün
UML'e Giriş
Giriş
UML yürürken ayaklarına bakmak gibidir. Genellikle bilinçsizce yapabileceğin şeyleri bilinçli ve açık hâle getirir Hepimiz kodları okuruz, yazarız, yorumlarız. Bizim hobimiz, işimiz, ya da mecburi alınan bir programlama dili dersini geçmek gibi sebepleri olabilir. Ve bir yazılım geliştiricisi genelde kodu yorumlamak veya diyagramlarını çizmeye pek eğilimli değildir. Direkt kodun kendisini okumak daha akla yatkın gelir. Ama bazı faydaları vardır ki onu da göz önünde bulundurup kullanmak gerekebilir. Hem forward engineering hem de reverse engineering için kullanılabilir.UML'i sadece yaptığın bir şey ile alakalı düşünmemek gerekir. Örneğin gece ormandasın ve yağmur yağmaya başladı. Ortalık kapkaranlık. Kendi ayaklarına baksan bile fayda etmeyecek çünkü hiçbir şey göremiyorsun. Ama birisi bir meşale ile yakınında olsaydı onun ışığını ya da ayak izlerini takip edebilirsin. Çamur, karanlık, ağaçlar, dikenli otlar arasında yolunu bulurdun. Bu benzetmeye örnek olarak,bir projeye başlandı şirkette. 5 yıl sonra, bu projeye önceden dahil olan insanlar gittiğinde ne olacak peki? Projeye daha sonra katılan herkes için bazı temel güncel belgelerin mevcut olması inanılmaz derecede yararlıdır. Şöyle bir gerçek de var. Bir projenin her kısmı tamamen method ve parametre isimleri ile dolu dolu açıklanmaya çalışılırsa bunun sürdürülebilirliği ve bakımı zordur. Yine de sistemdeki yapılar arasındaki ilişkilerin temel diyagramlarını göstermek çok değerlidir. Genelde hiç kimse 50 sayfalık herşeyiyle tam oluşturulmuş bir UML diyagramını okumayı istemez. Bu yüzden ölçülü bir şekilde bir sistemin UML diyagramını oluşturmak gerekir. UML'in bir diğer yaralı olduğu nokta da şu: Yazılım departmanında senior olan bi geliştirici bir yapıyı dizayn etmekten sorumlu olup, tasarımın uygulamasını junior geliştiriciye bırakması.
Genel olarak UML'in ne için kullanıldığı, eşsiz olmayan benzetmelerle (based on stack overflow entries) açıklanmaya çalışıldı. Şimdi UML'in ne olduğuna bakalım.
Gelişme
UML bir modelleme dilidir. Temel amacı tasarlanan bir sistemi görselleştirmektir. UML bir programı dili değil, görsel bir dildir aslında. UML nesne yönelimli programlama ile bağlantılıdır ve elementler arası ilişkinin görselleştirilmesini diyagramlar ile sağlar. Bu diyagramlar ikiye ayrılabilir: Behavior (Davranış) diyagramı, Structural (Yapısal) diagram.Class Diagrams
UML'i herşeyiyle tam olarak anlatmak aşırı uzun ve zaman alacak bir şey olduğu için sözü direkt class diagrams'lara getirelim. Class diyagramları her nesne yönelimli metodun ana "building block"larıdır. Sınıflar arası ilişkileri, arayüz kullanımını, ve diğer sınıflar ile alakalı şeyleri göstermek için kullanılır. Class diyagramları şu yapılardan oluşmaktadır:- Class {name, attribute, method}
- Objects
- Interface
- Relationships {inheritance (generalization), implementation (realization), association, dependency, composition, aggregation}
- Associaitons {bidirectional, unidirectional}
Metotların ve özelliklerin görünümünü (public, private, protected) belirtebilceğimiz 3 tip "modifier" vardır: "+" public görünürlük sağlar (Herkes için). "#" protected görünürlük sağlar (Kendisinden türetilen sınıflarda da görünebilmesi için). "-" private görünürlük sağlar (Sadece kendisi için).