Design Patterns
TypeScript, JavaScript tabanlı bir programlama dilidir ve genellikle büyük ölçekli uygulamaların geliştirilmesi için kullanılır. TypeScript, JavaScript'in yanı sıra statik tür sistemine sahip olduğu için, kodunuzun daha güvenli ve sürdürülebilir olmasına olanak tanır. Bu nedenle, TypeScript projelerinde tasarım kalıpları, kodun düzenlenmesi, sürdürülebilirliği artırmak ve hataları azaltmak için önemli bir rol oynar.
Design pattern'lar, belirli problemleri çözmek veya belirli durumlarla başa çıkmak için geliştirilmiş genel çözüm şablonlarıdır. TypeScript tasarım kalıpları da aynı prensiplere dayanır ve genellikle aşağıdaki amaçlara hizmet eder:
- Düzen ve Organizasyon: Design pattern'lar, kodunuzun düzenli ve anlaşılır olmasına yardımcı olabilir. Projenizin büyüklüğü arttıkça, düzenli bir yapıya sahip olmak, kodunuzun bakımını kolaylaştırır.
- Tekrar Kullanım: Design pattern'lar, belirli bir problemi çözmek için genel bir çözüm sağladığından, bu çözümleri başka yerlerde tekrar kullanabilirsiniz. Bu, kodunuzun daha az tekrar içermesine ve daha az hata içermesine yardımcı olabilir.
- Hata Azaltma: Design pattern'lar, belirli bir problemin çözümü için test edilmiş ve doğrulanmış bir yaklaşım sunar. Bu nedenle, bu kalıpları kullanmak, hataları azaltmanıza yardımcı olabilir.
- Değişikliklere Direnç: Tasarım kalıpları, değişikliklere karşı dirençli olacak şekilde tasarlanabilir. Yani, projenizde yapılan değişikliklerin diğer kısımları etkileme olasılığını azaltabilir.
- Performans: Design pattern'lar, belirli bir problemin çözümü için optimize edilmiş bir yaklaşım sunar. Bu nedenle, bu kalıpları kullanmak, performansı artırmanıza yardımcı olabilir.
- Ölçeklenebilirlik: Design pattern'lar, belirli bir problemin çözümü için optimize edilmiş bir yaklaşım sunar. Bu nedenle, bu kalıpları kullanmak, performansı artırmanıza yardımcı olabilir.
- Esneklik: Design pattern'lar, belirli bir problemin çözümü için optimize edilmiş bir yaklaşım sunar. Bu nedenle, bu kalıpları kullanmak, performansı artırmanıza yardımcı olabilir.
Singletons Deseni
Singleton deseni, bir sınıfın yalnızca bir örneğinin olduğundan emin olmayı amaçlayan bir tasarım desenidir. TypeScript dilinde de Singleton deseni kullanılabilir, ancak TypeScript'in doğası gereği, bazı dillerdeki gibi tam anlamıyla private constructor (özel kurucu) kullanmak mümkün değildir. Bununla birlikte, Singleton desenini uygulamak için bazı yöntemler vardır.
İlk olarak, bir sınıfın yalnızca bir örneğini tutmak için bir statik özellik kullanabilirsiniz. Aşağıda, TypeScript ile basit bir Singleton örneği gösterilmektedir:
Singleton
class Singleton {
private static instance: Singleton | null = null
private constructor() {
// private constructor
}
public static getInstance(): Singleton {
if (!Singleton.instance) {
Singleton.instance = new Singleton()
}
return Singleton.instance
}
public doSomething(): void {
console.log("Singleton is doing something")
}
}
// Kullanım örneği
const singleton1 = Singleton.getInstance()
const singleton2 = Singleton.getInstance()
console.log(singleton1 === singleton2) // true
Bu örnekte, getInstance metodu ile Singleton sınıfının tek bir örneğini alabilirsiniz. Eğer daha önce bir örnek oluşturulmamışsa, getInstance metodu yeni bir örnek oluşturur ve bu örneği döndürür. Eğer önce bir örnek oluşturulmuşsa, mevcut örneği döndürür.
Ancak, bu yöntem tam anlamıyla private constructor kullanmaz ve new Singleton() ifadesi herhangi bir yerde çağrılabilir. Bu nedenle, sınıfın oluşturulmasını sınırlamak için belirli bir yöntem kullanılması önemlidir.
Bu örnekte, TypeScript'in private constructor özelliği olmadığı için private constructor() kullanılarak sınıfın dışından doğrudan bir örneği oluşturulması engellenir. Ancak, dikkatli olunması ve Singleton desenini doğru bir şekilde uygulamak için belirli durumları düşünmek önemlidir.
Factory Deseni
Factory deseni (Factory Pattern), bir nesne yaratma sürecini kapsülleme amacı güden bir creational (yaratıcı) tasarım desenidir. Bu desen, nesne oluşturma mantığını ana sınıftan ayırarak, bir alt sınıfın hangi sınıfın nesnesini oluşturacağını belirlemesine izin verir.
Factory deseni, genellikle bir arayüz veya soyut bir sınıf üzerinde duran bir "fabrika" sınıfını içerir. Alt sınıflar bu arayüzü veya soyut sınıfı uygular ve kendi özelleştirilmiş nesnelerini oluşturmak için gerekli yöntemleri sağlarlar.
İşte TypeScript ile basit bir Factory deseni örneği:
Factory
// Ürün arayüzü veya soyut sınıf
interface Product {
operation(): string
}
// ConcreteProduct1
class ConcreteProduct1 implements Product {
operation(): string {
return "ConcreteProduct1 operation"
}
}
// ConcreteProduct2
class ConcreteProduct2 implements Product {
operation(): string {
return "ConcreteProduct2 operation"
}
}
// Fabrika arayüzü veya soyut sınıf
interface Creator {
factoryMethod(): Product
}
// ConcreteCreator1
class ConcreteCreator1 implements Creator {
factoryMethod(): Product {
return new ConcreteProduct1()
}
}
// ConcreteCreator2
class ConcreteCreator2 implements Creator {
factoryMethod(): Product {
return new ConcreteProduct2()
}
}
// Kullanım
function clientCode(creator: Creator): void {
const product = creator.factoryMethod()
console.log(product.operation())
}
// Kullanım örneği
const creator1 = new ConcreteCreator1()
clientCode(creator1)
const creator2 = new ConcreteCreator2()
clientCode(creator2)
Bu örnekte, Product arayüzü veya soyut sınıf, oluşturulacak nesnelerin ortak davranışlarını tanımlar. ConcreteProduct1 ve ConcreteProduct2 sınıfları bu arayüzü uygular ve kendi özel davranışlarını sağlarlar.
Creator arayüzü veya soyut sınıf, fabrika metodunu tanımlar. Bu metod, alt sınıfların hangi ürünü oluşturacaklarını belirler. ConcreteCreator1 ve ConcreteCreator2 sınıfları, bu arayüzü uygular ve kendi factoryMethod implementasyonlarını sağlarlar.
Son olarak, clientCode fonksiyonu, bir Creator örneği alır ve bu creatörün factoryMethod'unu çağırarak belirli bir ürünü oluşturur ve kullanır. Bu şekilde, istemci kodu hangi ürünü kullanacağını bilmeksizin bir nesne yaratabilir.
Observer Deseni
Observer deseni, bir nesnenin durumundaki değişiklikleri takip etmek isteyen bir dizi nesnenin (gözlemcilerin) bir nesneye abone olmasını sağlayan bir tasarım desenidir. Bu desen, bir nesnenin durumunda değişiklik olduğunda bağımlı nesneleri otomatik olarak güncellemek için kullanılır. Bu durum, bir nesnenin durumu değiştikçe diğer nesnelerin tepki vermesini sağlar.
Bu desenin ana bileşenleri şunlardır:
-
Subject (Konu): Gözlemleyicileri (observers) içeren bir nesne. Durumu değiştikçe gözlemcilerini haberdar eder.
-
Observer (Gözlemci): Bir konu üzerinde değişiklikleri takip etmek isteyen arayüze sahip nesneler.
Observer deseni, yaygın olarak kullanıcı arayüzleri, olay sistemleri ve veri alma/gönderme gibi birçok senaryoda kullanılır.
İşte TypeScript ile basit bir Observer deseni örneği:
Observer
// Subject (Konu) arayüzü veya soyut sınıf
interface Subject {
addObserver(observer: Observer): void
removeObserver(observer: Observer): void
notifyObservers(): void
}
// ConcreteSubject (Somut Konu)
class ConcreteSubject implements Subject {
private observers: Observer[] = []
private state: number = 0
addObserver(observer: Observer): void {
this.observers.push(observer)
}
removeObserver(observer: Observer): void {
const index = this.observers.indexOf(observer)
if (index !== -1) {
this.observers.splice(index, 1)
}
}
notifyObservers(): void {
for (const observer of this.observers) {
observer.update(this.state)
}
}
setState(state: number): void {
this.state = state
this.notifyObservers()
}
}
// Observer (Gözlemci) arayüzü veya soyut sınıf
interface Observer {
update(state: number): void
}
// ConcreteObserver (Somut Gözlemci)
class ConcreteObserver implements Observer {
private name: string
constructor(name: string) {
this.name = name
}
update(state: number): void {
console.log(`${this.name} notified with state: ${state}`)
}
}
// Kullanım
const subject = new ConcreteSubject()
const observer1 = new ConcreteObserver("Observer 1")
const observer2 = new ConcreteObserver("Observer 2")
subject.addObserver(observer1)
subject.addObserver(observer2)
subject.setState(1)
subject.setState(2)
// Çıktı:
// Observer 1 notified with state: 1
// Observer 2 notified with state: 1
// Observer 1 notified with state: 2
// Observer 2 notified with state: 2
Bu örnekte, ConcreteSubject bir Subject arayüzünü uygular ve
ConcreteObserver bir Observer arayüzünü uygular. ConcreteSubject,
içerdiği gözlemcileri (observers) takip eder ve durumu değiştiğinde bu
gözlemcilere haber verir. ConcreteObserver ise durumu alır ve kendine özgü
bir şekilde tepki verir.
Decorator Deseni
Decorator deseni, nesne üzerinde dinamik olarak davranış eklemek için kullanılan bir tasarım desenidir. Bu desen, bir nesneyi sarmalayarak (wrap) ve bu sarmalayıcı nesneye ek işlevsellik ekleyerek çalışır. Bu sayede, nesne üzerinde yapılan değişiklikler, nesnenin sarmalayıcıları aracılığıyla etki eder ve bu değişiklikler çalışma zamanında gerçekleştirilebilir.
Decorator deseni, Open/Closed prensibine (bir sınıfın davranışını değiştirmek için sınıfın değiştirilmemesi) uyan ve bir nesnenin davranışını genişletmek veya değiştirmek istediğimizde tercih ettiğimiz bir desenidir.
İşte TypeScript ile basit bir Decorator deseni örneği:
Decorator
// Component (Bileşen) arayüzü veya soyut sınıf
interface Coffee {
cost(): number
description(): string
}
// ConcreteComponent (Somut Bileşen)
class SimpleCoffee implements Coffee {
cost(): number {
return 5
}
description(): string {
return "Simple coffee"
}
}
// Decorator (Dekoratör) arayüzü veya soyut sınıf
abstract class CoffeeDecorator implements Coffee {
protected decoratedCoffee: Coffee
constructor(coffee: Coffee) {
this.decoratedCoffee = coffee
}
cost(): number {
return this.decoratedCoffee.cost()
}
description(): string {
return this.decoratedCoffee.description()
}
}
// ConcreteDecorator (Somut Dekoratör)
class MilkDecorator extends CoffeeDecorator {
constructor(coffee: Coffee) {
super(coffee)
}
cost(): number {
return this.decoratedCoffee.cost() + 2
}
description(): string {
return this.decoratedCoffee.description() + ", with milk"
}
}
// ConcreteDecorator (Somut Dekoratör)
class SugarDecorator extends CoffeeDecorator {
constructor(coffee: Coffee) {
super(coffee)
}
cost(): number {
return this.decoratedCoffee.cost() + 1
}
description(): string {
return this.decoratedCoffee.description() + ", with sugar"
}
}
// Kullanım
const simpleCoffee: Coffee = new SimpleCoffee()
console.log(
`Cost: $${simpleCoffee.cost()}, Description: ${simpleCoffee.description()}`
)
const milkCoffee: Coffee = new MilkDecorator(new SimpleCoffee())
console.log(
`Cost: $${milkCoffee.cost()}, Description: ${milkCoffee.description()}`
)
const sugarMilkCoffee: Coffee = new SugarDecorator(
new MilkDecorator(new SimpleCoffee())
)
console.log(
`Cost: $${sugarMilkCoffee.cost()}, Description: ${sugarMilkCoffee.description()}`
)
Bu örnekte, Coffee arayüzü veya soyut sınıf, dekore edilecek nesnenin temel davranışını belirtir. SimpleCoffee sınıfı, bu arayüzü uygular ve temel kahve davranışını sağlar.
CoffeeDecorator soyut sınıfı, dekoratörlerin uygulanmasını sağlar ve ConcreteDecorator sınıfları, gerçek dekorasyonları uygular. Örneğin, MilkDecorator sınıfı süt eklerken, SugarDecorator sınıfı şeker ekler.
Sonuç olarak, SimpleCoffee nesnesi üzerine MilkDecorator ve SugarDecorator sarmalayıcıları ekleyerek yeni özellikler ekleyebilir ve nesnenin davranışını değiştirebilirsiniz.
Prototype Deseni
Prototype deseni, nesnelerin kopyalarını oluşturmak için kullanılan bir tasarım desenidir. Bu desen, nesne oluşturma sürecini bir prototip nesnesinden türetilen kopyalar üzerinden gerçekleştirir. Bu sayede, bir nesnenin yeni kopyalarını oluşturmak için sınıf hiyerarşilerini yeniden oluşturmak gerekmez.
Prototype deseni, özellikle nesnenin karmaşık bir inşa sürecine sahip olduğu durumlarda kullanışlıdır. Bu desen, nesne oluşturma maliyetini düşürerek ve yeni nesnelerin başka nesnelerle aynı başlangıç durumlarına sahip olmasını sağlayarak performans ve bellek kullanımını iyileştirebilir.
İşte TypeScript ile basit bir Prototype deseni örneği:
Prototype
// Prototype (Prototip) arayüzü veya soyut sınıf
interface Cloneable {
clone(): Cloneable
}
// ConcretePrototype (Somut Prototip)
class ConcretePrototype implements Cloneable {
private property1: string
private property2: number
constructor(property1: string, property2: number) {
this.property1 = property1
this.property2 = property2
}
clone(): Cloneable {
// Yeni bir kopya oluşturulur ve mevcut özellikler kopyalanır
return new ConcretePrototype(this.property1, this.property2)
}
// Diğer metotlar ve özellikler...
}
// Kullanım
const prototypeInstance1 = new ConcretePrototype("Value 1", 42)
const clonedInstance1 = prototypeInstance1.clone()
console.log(prototypeInstance1) // Orjinal nesne
console.log(clonedInstance1) // Kopya nesne
Bu örnekte, Cloneable arayüzü veya soyut sınıf, klonlama yeteneğini belirtir. ConcretePrototype sınıfı, bu arayüzü uygular ve klonlanabilir nesnenin yapısını ve davranışını tanımlar.
Kullanım örneğinde, prototypeInstance1 adlı bir orijinal nesne oluşturulur. Ardından, bu nesnenin clone metodunu kullanarak bir kopyası olan clonedInstance1 oluşturulur. Bu sayede, clonedInstance1 ve prototypeInstance1 nesneleri aynı başlangıç durumlarına sahip olur, ancak bağımsız nesnelerdir.
Prototype deseni, özellikle nesnenin içeriği karmaşık ve nesnenin kopyalanması maliyetli olduğu durumlarda kullanılır.
Builder Deseni
Builder deseni, bir nesnenin karmaşık yapısını adım adım oluşturan ve nesnenin farklı temsil biçimlerini üretebilen bir tasarım desenidir. Builder deseni, bir nesnenin inşa edilmesi sürecini ayrı bir sınıfa taşıyarak, nesnenin farklı özelliklerini kontrol etmeyi ve bu özelliklere göre nesneyi oluşturmayı sağlar.
İşte TypeScript ile Builder desenini uygulamak için basit bir örnek:
Builder
// Üretilen nesne için temsil edilecek arayüz
class Product {
private parts: string[] = [];
addPart(part: string): void {
this.parts.push(part);
}
showParts(): void {
console.log(`Product Parts: ${this.parts.join(', ')}`);
}
}
// Builder arayüzü
interface Builder {
buildPartA(): void;
buildPartB(): void;
getResult(): Product;
}
// ConcreteBuilder sınıfı: Builder arayüzünü uygular
class ConcreteBuilder implements Builder {
private product: Product = new Product();
buildPartA(): void {
this.product.addPart('PartA');
}
buildPartB(): void {
this.product.addPart('PartB');
}
getResult(): Product {
return this.product;
}
}
// Director sınıfı: Builder'ı kullanarak nesne inşa eder
class Director {
private builder: Builder;
constructor(builder: Builder) {
this.builder = builder;
}
construct(): void {
this.builder.buildPartA();
this.builder.buildPartB();
}
}
// Kullanım
const builder: Builder = new ConcreteBuilder();
const director: Director = new Director(builder);
director.construct();
const product: Product = builder.getResult();
product.showParts();
Bu örnekte, Product sınıfı, oluşturulacak nesneyi temsil eder. Builder arayüzü, nesnenin inşa edilme sürecini tanımlar. ConcreteBuilder sınıfı, Builder arayüzünü uygular ve nesnenin farklı parçalarını ekler. Director sınıfı, Builder'ı kullanarak nesneyi inşa eder.
Bu sayede, Director sınıfı, nesnenin hangi parçalardan oluşacağını belirler, ancak hangi parçaların eklenip çıkarılacağı ConcreteBuilder sınıfında belirlenir. Bu, nesne oluşturma sürecini daha esnek hale getirir ve farklı varyasyonlarda nesneler oluşturmak için kullanışlıdır.
Facade Deseni
Facade deseni, bir alt sistemdeki nesnelerin karmaşıklığını gizleyen ve bir arayüz sağlayan bir tasarım desenidir. Bu desen, bir alt sistemin iç yapısını müşterilere karşı gizleyerek, alt sistemin kullanımını daha basitleştirir ve daha yüksek seviyeli bir arabirim sunar.
İşte TypeScript ile basit bir Facade deseni örneği:
Facade
// Alt sistem sınıfları
class SubsystemA {
operationA(): string {
return 'Subsystem A operation';
}
}
class SubsystemB {
operationB(): string {
return 'Subsystem B operation';
}
}
class SubsystemC {
operationC(): string {
return 'Subsystem C operation';
}
}
// Facade sınıfı
class Facade {
private subsystemA: SubsystemA;
private subsystemB: SubsystemB;
private subsystemC: SubsystemC;
constructor() {
this.subsystemA = new SubsystemA();
this.subsystemB = new SubsystemB();
this.subsystemC = new SubsystemC();
}
operation(): string {
let result = 'Facade initiates operations:\n';
result += this.subsystemA.operationA() + '\n';
result += this.subsystemB.operationB() + '\n';
result += this.subsystemC.operationC();
return result;
}
}
// Kullanım
const facade = new Facade();
const result = facade.operation();
console.log(result);
Bu örnekte, SubsystemA, SubsystemB, ve SubsystemC alt sistem sınıflarını temsil eder. Bu sınıflar, karmaşık işlemleri gerçekleştirir. Facade sınıfı ise bu alt sistemleri yöneten ve müşterilere basitleştirilmiş bir arabirim sunan sınıftır.
Facade deseni, alt sistemlerin iç yapısındaki karmaşıklığı gizleyerek, müşterinin daha basit bir şekilde kullanımını sağlar. Bu, alt sistemlerdeki değişikliklerin müşteri kodunu etkilememesini sağlar. Facade, alt sistemlerin bir arada çalışmasını koordine edebilir ve müşteriye tek bir noktadan erişim sağlar.
Bu desen, bir uygulamanın çeşitli bileşenlerini birleştiren ve müşteri kodu için basit bir arayüz sunan durumlarda özellikle faydalıdır.
Proxy Deseni
Proxy deseni, bir nesnenin yerine geçen bir kontrol noktasıdır ve bu sayede gerçek nesneyle olan etkileşimi kontrol eder. Bu desen, performans optimizasyonu, erişim kontrolü, giriş doğrulaması veya bir nesnenin yaratılma maliyetinden kaçınma gibi senaryolarda kullanılır.
İşte TypeScript ile basit bir Proxy deseni örneği:
Proxy
// Temel öznenin arayüzü
interface Subject {
request(): void;
}
// Gerçek özne sınıfı
class RealSubject implements Subject {
request(): void {
console.log('RealSubject: Handling request.');
}
}
// Proxy sınıfı
class Proxy implements Subject {
private realSubject: RealSubject;
constructor(realSubject: RealSubject) {
this.realSubject = realSubject;
}
private checkAccess(): boolean {
console.log('Proxy: Checking access.');
// Burada gerçek bir erişim kontrolü gerçekleştirilebilir.
return true;
}
private logAccess(): void {
console.log('Proxy: Logging access.');
}
request(): void {
if (this.checkAccess()) {
this.realSubject.request();
this.logAccess();
}
}
}
// Kullanım
const realSubject = new RealSubject();
const proxy = new Proxy(realSubject);
// Gerçek özneye doğrudan erişim
realSubject.request();
// Proxy üzerinden erişim
proxy.request();
Bu örnekte, Subject arayüzü, gerçek öznenin ve proxy'nin uyguladığı arayüzdür. RealSubject sınıfı, gerçek işlemleri gerçekleştiren sınıftır. Proxy sınıfı ise erişim kontrolü ve loglama gibi ek işlevsellik ekler.
Proxy deseni, gerçek nesnenin yaratılması veya kullanılması için ek bir kontrol ekler, böylece gerçek nesnenin yaratılma maliyetinden veya gereksiz işlemlerden kaçınılabilir. Aynı zamanda, erişim kontrolü gibi işlevselliği gerçekleştirebilir ve gerçek nesnenin davranışını kontrol edebilir. Bu sayede, istemci kodu değiştirmeden önce veya sonra ek işlevselliği uygulamak daha kolay olabilir.
Iterator Deseni
Iterator deseni, bir nesne koleksiyonu üzerinde sırayla gezinmek için bir arayüz sağlayan ve bu koleksiyonun altında bulunan elemanlara erişimi standartlaştıran bir tasarım desenidir. Iterator deseni, koleksiyonun iç yapısını gizler ve istemcilerin koleksiyon üzerinde gezinmeyi basitleştirir.
İşte TypeScript ile basit bir Iterator deseni örneği:
Iterator
// Iterator arayüzü
interface Iterator<T> {
next(): T;
hasNext(): boolean;
}
// Aggregate arayüzü
interface Aggregate<T> {
createIterator(): Iterator<T>;
}
// ConcreteAggregate sınıfı
class ConcreteAggregate<T> implements Aggregate<T> {
private items: T[] = [];
addItem(item: T): void {
this.items.push(item);
}
createIterator(): Iterator<T> {
return new ConcreteIterator<T>(this);
}
getItems(): T[] {
return this.items;
}
}
// ConcreteIterator sınıfı
class ConcreteIterator<T> implements Iterator<T> {
private aggregate: ConcreteAggregate<T>;
private index: number = 0;
constructor(aggregate: ConcreteAggregate<T>) {
this.aggregate = aggregate;
}
next(): T {
const currentItem = this.aggregate.getItems()[this.index];
this.index++;
return currentItem;
}
hasNext(): boolean {
return this.index < this.aggregate.getItems().length;
}
}
// Kullanım
const aggregate = new ConcreteAggregate<number>();
aggregate.addItem(1);
aggregate.addItem(2);
aggregate.addItem(3);
const iterator = aggregate.createIterator();
while (iterator.hasNext()) {
const currentItem = iterator.next();
console.log(`Item: ${currentItem}`);
}
Bu örnekte, Iterator arayüzü, koleksiyon üzerinde gezinme işlemlerini tanımlar. Aggregate arayüzü ise bu koleksiyonları oluşturacak sınıflar için bir arayüz sağlar. ConcreteAggregate sınıfı, koleksiyonu temsil eder ve ConcreteIterator sınıfı, bu koleksiyon üzerinde gezinme işlemlerini uygular.
Iterator deseni, koleksiyonun iç yapısını gizler ve istemcilerin koleksiyon üzerinde dolaşmasını standartlaştırır. Bu sayede, koleksiyonun iç yapısı değişse bile istemci kodunu değiştirmeye gerek kalmaz. Aynı zamanda, koleksiyon üzerinde farklı gezinme algoritmaları uygulanabilir ve istemci kodu değiştirmeden bu algoritmalar kullanılabilir.
Adapter Deseni
Adapter deseni, bir sınıfın arabirimini, başka bir sınıfın beklediği arabirime dönüştüren bir yapısal tasarım desenidir. Bu desen, uyumsuz arabirimlere sahip sınıfların birbirleriyle etkileşimini sağlar.
İşte TypeScript ile basit bir Adapter deseni örneği:
Adapter
// Hedef Arabirim
interface Target {
request(): string;
}
// Hedefe Uygun Sınıf
class ConcreteTarget implements Target {
request(): string {
return 'ConcreteTarget: The default behavior.';
}
}
// Kaynak Sınıf
class Adaptee {
specificRequest(): string {
return 'Adaptee: The specific behavior.';
}
}
// Adaptör Sınıfı
class Adapter implements Target {
private adaptee: Adaptee;
constructor(adaptee: Adaptee) {
this.adaptee = adaptee;
}
request(): string {
return `Adapter: (Translated) ${this.adaptee.specificRequest()}`;
}
}
// Kullanım
const target: Target = new ConcreteTarget();
console.log(target.request());
const adaptee: Adaptee = new Adaptee();
const adapter: Target = new Adapter(adaptee);
console.log(adapter.request());
Bu örnekte, Target arabirimi, istemcinin beklediği standart arabirimi temsil eder. ConcreteTarget sınıfı, bu arabirimi uygular. Adaptee sınıfı, mevcut ancak istemcinin beklediği arabirime sahip olmayan bir sınıfı temsil eder. Adapter sınıfı, Adaptee sınıfını kullanarak Target arabirimini uygular. Bu sayede, Adapter sınıfı, Adaptee sınıfının özelliklerini Target arabirimine uyacak şekilde dönüştürür.
Adapter deseni, mevcut sınıfların uyumlu bir şekilde birlikte çalışmasını sağlar ve mevcut kodun yeniden kullanılmasını kolaylaştırır. Adapter, uyumsuz sınıflar arasında bir ara birimi sağlayarak sistemdeki esnekliği artırır.
Bridge Deseni
Bridge deseni, bir sınıfın uygulama ve soyutlama detaylarını ayıran ve bu iki hiyerarşiyi birbirinden bağımsız hale getiren bir yapısal tasarım desenidir. Bu desen, genişletilebilir ve değiştirilebilir bir tasarım oluşturmayı amaçlar.
İşte TypeScript ile basit bir Bridge deseni örneği:
Bridge
// Soyut İmplementasyon
interface Implementor {
operationImpl(): string;
}
// Somut İmplementasyon 1
class ConcreteImplementorA implements Implementor {
operationImpl(): string {
return 'ConcreteImplementorA: Operation implementation.';
}
}
// Somut İmplementasyon 2
class ConcreteImplementorB implements Implementor {
operationImpl(): string {
return 'ConcreteImplementorB: Operation implementation.';
}
}
// Soyut Abstraksiyon
abstract class Abstraction {
protected implementor: Implementor;
constructor(implementor: Implementor) {
this.implementor = implementor;
}
abstract operation(): string;
}
// Somut Abstraksiyon 1
class RefinedAbstractionA extends Abstraction {
operation(): string {
return `RefinedAbstractionA: ${this.implementor.operationImpl()}`;
}
}
// Somut Abstraksiyon 2
class RefinedAbstractionB extends Abstraction {
operation(): string {
return `RefinedAbstractionB: ${this.implementor.operationImpl()}`;
}
}
// Kullanım
const implementorA: Implementor = new ConcreteImplementorA();
const abstractionA: Abstraction = new RefinedAbstractionA(implementorA);
console.log(abstractionA.operation());
const implementorB: Implementor = new ConcreteImplementorB();
const abstractionB: Abstraction = new RefinedAbstractionB(implementorB);
console.log(abstractionB.operation());
Bu örnekte, Bridge deseni iki ayrı hiyerarşi olan soyut implementasyon (Implementor) ve soyut abstraksiyon (Abstraction) içerir. ConcreteImplementorA ve ConcreteImplementorB sınıfları, Implementor arayüzünü uygular ve farklı implementasyonları temsil eder. RefinedAbstractionA ve RefinedAbstractionB sınıfları, soyut abstraksiyonu uygular ve bu abstraksiyonu genişletir.
Bridge deseni, soyut abstraksiyon ve soyut implementasyon arasındaki bağlantıyı kopararak her iki hiyerarşiyi de ayrı ayrı genişletebilir ve değiştirebilir bir yapı sağlar. Bu sayede, kodun daha esnek, genişletilebilir ve bakımı daha kolay hale gelir.
Composite Deseni
Bridge deseni, bir sınıfın uygulama ve soyutlama detaylarını ayıran ve bu iki hiyerarşiyi birbirinden bağımsız hale getiren bir yapısal tasarım desenidir. Bu desen, genişletilebilir ve değiştirilebilir bir tasarım oluşturmayı amaçlar.
İşte TypeScript ile basit bir Bridge deseni örneği:
Composite
// Component arayüzü
interface Component {
operation(): string;
}
// Leaf sınıfı: Tek bir nesneyi temsil eder
class Leaf implements Component {
private name: string;
constructor(name: string) {
this.name = name;
}
operation(): string {
return `Leaf(${this.name}): Operation`;
}
}
// Composite sınıfı: Grup nesnesini temsil eder
class Composite implements Component {
private children: Component[] = [];
add(component: Component): void {
this.children.push(component);
}
remove(component: Component): void {
this.children = this.children.filter((c) => c !== component);
}
operation(): string {
const results: string[] = [];
for (const child of this.children) {
results.push(child.operation());
}
return `Composite: [${results.join(', ')}]`;
}
}
// Kullanım
const leaf1 = new Leaf('Leaf1');
const leaf2 = new Leaf('Leaf2');
const leaf3 = new Leaf('Leaf3');
const composite = new Composite();
composite.add(leaf1);
composite.add(leaf2);
const composite2 = new Composite();
composite2.add(leaf3);
composite2.add(composite);
console.log(composite2.operation());
Bu örnekte, Component arayüzü, hem yaprak nesneleri (Leaf) hem de grup nesneleri (Composite) için bir arayüz sağlar. Leaf sınıfı, tek bir nesneyi temsil eder. Composite sınıfı ise grup nesnesini temsil eder ve içinde birden çok Component saklar.
Composite deseni, tek bir nesne ile grup nesnelerini kullanma yeteneği sağlar. Bu sayede, müşteri kodu tek bir nesne veya grup nesneleriyle aynı şekilde etkileşimde bulunabilir. Composite deseni, hiyerarşik nesneleri bir arada kullanmak istediğiniz durumlarda kullanışlıdır.
Flyweight Deseni
Flyweight deseni, bir nesnenin paylaşılabilir (tekrar kullanılabilir) özelliklerini ve durumunu ayrıştıran bir yapısal tasarım desenidir. Bu desen, büyük miktarlarda benzer nesneler oluşturulduğunda ve bu nesnelerin birçoğunun aynı verilere sahip olduğunda kullanılır. Böylece, bellek kullanımını azaltarak performansı artırmayı amaçlar.
İşte TypeScript ile basit bir Flyweight deseni örneği:
Flyweight
// Flyweight arayüzü
interface Flyweight {
operation(extrinsicState: string): string;
}
// ConcreteFlyweight sınıfı: Paylaşılan nesne
class ConcreteFlyweight implements Flyweight {
private intrinsicState: string;
constructor(intrinsicState: string) {
this.intrinsicState = intrinsicState;
}
operation(extrinsicState: string): string {
return `ConcreteFlyweight: Intrinsic(${this.intrinsicState}), Extrinsic(${extrinsicState})`;
}
}
// Flyweight Factory sınıfı: Paylaşılan nesneleri yönetir
class FlyweightFactory {
private flyweights: { [key: string]: Flyweight } = {};
getFlyweight(key: string): Flyweight {
if (!this.flyweights[key]) {
this.flyweights[key] = new ConcreteFlyweight(key);
}
return this.flyweights[key];
}
getFlyweightCount(): number {
return Object.keys(this.flyweights).length;
}
}
// Kullanım
const factory = new FlyweightFactory();
const flyweight1 = factory.getFlyweight('A');
console.log(flyweight1.operation('X'));
const flyweight2 = factory.getFlyweight('B');
console.log(flyweight2.operation('Y'));
const flyweight3 = factory.getFlyweight('A');
console.log(flyweight3.operation('Z'));
console.log(`Number of flyweights created: ${factory.getFlyweightCount()}`);
Bu örnekte, Flyweight arayüzü paylaşılabilir nesnelerin işlevselliğini tanımlar. ConcreteFlyweight sınıfı, bu arayüzü uygular ve paylaşılan nesnenin özelliklerini temsil eder. FlyweightFactory sınıfı, paylaşılan nesneleri yönetir ve bu nesneleri oluşturur. Kullanıcılar bu fabrikadan paylaşılan nesneleri alabilir.
Flyweight deseni, benzer nesnelerin paylaşılabilir özelliklerini bir araya getirerek bellek kullanımını azaltır. Bu özellikle büyük veri setleri veya benzer nesnelerin çok sayıda oluşturulduğu senaryolarda performans artışı sağlar.
Command Deseni
Command deseni, bir nesnenin bir isteği (komutu) içerdiği bir nesne oluşturarak ve bu isteği bir nesne tarafından çağrılmaya elverişli bir şekilde sarmalayarak, komutları parametreleştirme, sıralama ve sıralı bir şekilde istemci nesne tarafından geri alma gibi işlemleri sağlayan bir davranışsal tasarım desenidir.
İşte TypeScript ile basit bir Command deseni örneği:
Command
// Command arayüzü
interface Command {
execute(): void;
}
// Receiver sınıfı: Gerçek işlemleri gerçekleştiren nesne
class Receiver {
performAction(): void {
console.log('Receiver: Performing action');
}
}
// ConcreteCommand sınıfı: Command arayüzünü uygular ve Receiver'ı çağırır
class ConcreteCommand implements Command {
private receiver: Receiver;
constructor(receiver: Receiver) {
this.receiver = receiver;
}
execute(): void {
console.log('ConcreteCommand: Execute');
this.receiver.performAction();
}
}
// Invoker sınıfı: Command nesnelerini çağıran nesne
class Invoker {
private command: Command;
setCommand(command: Command): void {
this.command = command;
}
executeCommand(): void {
console.log('Invoker: Execute Command');
this.command.execute();
}
}
// Kullanım
const receiver = new Receiver();
const command = new ConcreteCommand(receiver);
const invoker = new Invoker();
invoker.setCommand(command);
invoker.executeCommand();
Bu örnekte, Command arayüzü, komutları temsil eder. ConcreteCommand sınıfı, bu arayüzü uygular ve bir Receiver nesnesini çağırarak gerçek işlemleri gerçekleştirir. Invoker sınıfı, Command nesnelerini çağırır ve bu sayede istemci tarafından işlemi başlatır.
Command deseni, istemci tarafından bir komutun tanımlanması, sıralanması ve gerçekleştirilmesi için esnek bir yol sağlar. Ayrıca, geri alma (undo) ve yeniden yapma (redo) gibi işlemleri yönetmek için de kullanılabilir.
Chain of Responsibility Deseni
"Chain of Responsibility" (Sorumluluk Zinciri) tasarım deseni, bir dizi nesnenin sorumluluklarını zincirleme bir şekilde taşıdığı ve bir isteğin bu zincir üzerinde sırayla işlendiği bir yapısal tasarım desenidir. Bu desen, bir isteği işleyebilecek olan nesnenin belirlenmesini ve bu isteğin işlenene kadar zincir üzerinde ilerlemesini sağlar.
Bu desenin temel amacı, isteği işleyecek nesnenin belirlenmesini sadeleştirmek ve isteği işleyen nesnelerin birbirinden bağımsız olmasını sağlamaktır. Her bir nesne, isteği işleyebiliyorsa işler, işleyemiyorsa isteği bir sonraki nesneye iletir.
İşte TypeScript ile basit bir Chain of Responsibility deseni örneği:
Chain of Responsibility
// Handler arayüzü
interface Handler {
setNext(handler: Handler): Handler;
handleRequest(request: string): string | null;
}
// BaseHandler sınıfı
abstract class BaseHandler implements Handler {
private nextHandler: Handler | null = null;
setNext(handler: Handler): Handler {
this.nextHandler = handler;
return handler;
}
handleRequest(request: string): string | null {
if (this.nextHandler) {
return this.nextHandler.handleRequest(request);
}
return null;
}
}
// ConcreteHandler sınıfları
class ConcreteHandlerA extends BaseHandler {
handleRequest(request: string): string | null {
if (request === 'A') {
return 'ConcreteHandlerA: Handling request A.';
}
return super.handleRequest(request);
}
}
class ConcreteHandlerB extends BaseHandler {
handleRequest(request: string): string | null {
if (request === 'B') {
return 'ConcreteHandlerB: Handling request B.';
}
return super.handleRequest(request);
}
}
// Kullanım
const handlerA = new ConcreteHandlerA();
const handlerB = new ConcreteHandlerB();
handlerA.setNext(handlerB);
console.log(handlerA.handleRequest('A')); // ConcreteHandlerA: Handling request A.
console.log(handlerA.handleRequest('B')); // ConcreteHandlerB: Handling request B.
console.log(handlerA.handleRequest('C')); // null
Bu örnekte, Handler arayüzü, isteği işleyen nesnelerin ortak metotlarını tanımlar. BaseHandler sınıfı, bu arayüzü uygular ve bir sonraki işleyiciyi belirleme ve isteği zincir üzerinde iletimi sağlar. ConcreteHandlerA ve ConcreteHandlerB sınıfları, isteği belirli koşullara göre işleyen somut işleyici sınıflarıdır.
Chain of Responsibility deseni, bir isteğin işlenmesi için birden fazla nesnenin sorumluluk almasına olanak tanır ve isteğin işlenme sırası belirli bir sırayla takip edilir. Bu sayede, isteği işleyecek olan nesne belirlenmiş olur ve bir nesne, isteği işleyemediyse bu istek bir sonraki nesneye iletilir.
Interpreter Deseni
"Interpreter" (Yorumlayıcı) tasarım deseni, belirli bir dilin veya problem alanının ifadelerini yorumlamak ve işlemek için kullanılan bir yapısal tasarım desenidir. Bu desen, belirli bir dil veya ifade setini temsil etmek için bir dil ağacı oluşturur ve bu ağacı yorumlayarak belirli işlemleri gerçekleştirir.
Bu desen genellikle dil işleme veya belirli bir dildeki ifadeleri yorumlama durumlarında kullanılır. Bir dildeki ifadelerin anlamını yorumlamak için bir dil ağacı oluşturulur ve bu ağaç üzerinde dolaşarak ifadeleri yorumlamak işlemi gerçekleştirilir.
İşte basit bir aritmetik ifade yorumlayıcısı örneği:
Interpreter
// AbstractExpression sınıfı
interface AbstractExpression {
interpret(): number;
}
// TerminalExpression sınıfı
class TerminalExpression implements AbstractExpression {
private value: number;
constructor(value: number) {
this.value = value;
}
interpret(): number {
return this.value;
}
}
// NonTerminalExpression sınıfı
class AddExpression implements AbstractExpression {
private leftExpression: AbstractExpression;
private rightExpression: AbstractExpression;
constructor(leftExpression: AbstractExpression, rightExpression: AbstractExpression) {
this.leftExpression = leftExpression;
this.rightExpression = rightExpression;
}
interpret(): number {
return this.leftExpression.interpret() + this.rightExpression.interpret();
}
}
// Context sınıfı
class Context {
private expression: AbstractExpression;
constructor(expression: AbstractExpression) {
this.expression = expression;
}
setExpression(expression: AbstractExpression): void {
this.expression = expression;
}
interpret(): number {
return this.expression.interpret();
}
}
// Kullanım
const expression = new AddExpression(
new TerminalExpression(5),
new AddExpression(new TerminalExpression(3), new TerminalExpression(2))
);
const context = new Context(expression);
console.log(context.interpret()); // 10
Bu örnekte, AbstractExpression arayüzü, yorumlayıcı ifadelerin ortak metotlarını tanımlar. TerminalExpression sınıfı, bir sayıyı temsil eden ifadeleri yorumlar. AddExpression sınıfı, iki ifadeyi toplar. Context sınıfı, bir ifadeyi yorumlamak için kullanılır.
Interpreter deseni, bir dilin veya ifade setinin yorumlanması gereken durumlarda kullanılır. Bu desen, ifadeleri oluşturmak ve yorumlamak için bir yol sağlar, bu da genellikle dil işleme veya belirli bir dildeki kurallara dayalı işlemler için uygundur.
Mediator Deseni
"Mediator" (Aracı) tasarım deseni, nesneler arasındaki etkileşimi düzenlemek ve bu nesneler arasındaki doğrudan bağımlılıkları azaltmak için kullanılan bir davranışsal tasarım desenidir. Bu desen, nesnelerin birbiriyle doğrudan iletişim kurmak yerine bir aracı üzerinden iletişim kurmalarını sağlar. Böylece, sistem içindeki nesneler birbirlerinden daha az bağımlı olur ve daha esnek bir yapı elde edilir.
Mediator deseni, bir sistemdeki nesneler arasındaki ilişkiyi düzenlemek ve ortak bir arabirim üzerinden etkileşim sağlamak amacıyla kullanılır. Bu, nesnelerin birbirleriyle sıkı bir şekilde bağlı olmadan birbirleriyle iletişim kurmalarına olanak tanır.
İşte TypeScript ile basit bir Mediator deseni örneği:
Mediator
// Mediator arayüzü
interface Mediator {
notify(sender: Colleague, event: string): void;
}
// ConcreteMediator sınıfı
class ConcreteMediator implements Mediator {
private colleague1: Colleague;
private colleague2: Colleague;
setColleague1(colleague1: Colleague): void {
this.colleague1 = colleague1;
}
setColleague2(colleague2: Colleague): void {
this.colleague2 = colleague2;
}
notify(sender: Colleague, event: string): void {
if (sender === this.colleague1) {
console.log(`Mediator notified: Colleague1 sends event - ${event}`);
this.colleague2.handleEvent(event);
} else if (sender === this.colleague2) {
console.log(`Mediator notified: Colleague2 sends event - ${event}`);
this.colleague1.handleEvent(event);
}
}
}
// Colleague arayüzü
interface Colleague {
setMediator(mediator: Mediator): void;
sendEvent(event: string): void;
handleEvent(event: string): void;
}
// ConcreteColleague sınıfı
class ConcreteColleague implements Colleague {
private mediator: Mediator;
setMediator(mediator: Mediator): void {
this.mediator = mediator;
}
sendEvent(event: string): void {
console.log(`Colleague sends event - ${event}`);
this.mediator.notify(this, event);
}
handleEvent(event: string): void {
console.log(`Colleague handles event - ${event}`);
}
}
// Kullanım
const mediator = new ConcreteMediator();
const colleague1 = new ConcreteColleague();
colleague1.setMediator(mediator);
const colleague2 = new ConcreteColleague();
colleague2.setMediator(mediator);
mediator.setColleague1(colleague1);
mediator.setColleague2(colleague2);
colleague1.sendEvent('Event A');
colleague2.sendEvent('Event B');
Bu örnekte, Mediator arayüzü, nesneler arasındaki etkileşimi sağlayan yöntemleri tanımlar. ConcreteMediator sınıfı, bu arayüzü uygular ve nesneler arasındaki etkileşimi düzenler. Colleague arayüzü, etkileşimde bulunan nesneleri temsil eder. ConcreteColleague sınıfı, bu arayüzü uygular ve bir olayı gönderip alabilen nesneleri temsil eder.
Mediator deseni, nesneler arasındaki bağımlılıkları azaltmak ve sistemdeki etkileşimleri düzenlemek için kullanılır. Bu sayede, sistemin daha esnek ve bakımı daha kolay olur.
Memento Deseni
"Memento" (Hatırlayıcı) tasarım deseni, bir nesnenin iç durumunu bir dizi halinde saklamak ve bu durumu daha sonra geri yüklemek için kullanılan bir davranışsal tasarım desenidir. Bu desen, bir nesnenin durumunu kaydedip geri yüklemek için kullanılır ve nesnenin durumunu anlık bir şekilde tutarak geri alma veya geçmiş durumlara dönme işlevselliği sağlar.
Memento deseni, bir nesnenin iç durumunu saklamak için bir "Memento" nesnesi kullanır. Bu nesne, nesnenin durumunu temsil eden bir arayüzü uygular. Ardından, bir "Caretaker" nesnesi, nesnenin durumunu saklamak ve geri yüklemek için bu "Memento" nesnesini kullanır.
İşte TypeScript ile basit bir Memento deseni örneği:
Memento
// Memento arayüzü
interface Memento {
getState(): string;
}
// ConcreteMemento sınıfı
class ConcreteMemento implements Memento {
private state: string;
constructor(state: string) {
this.state = state;
}
getState(): string {
return this.state;
}
}
// Originator sınıfı
class Originator {
private state: string;
setState(state: string): void {
console.log(`Originator: Setting state to ${state}`);
this.state = state;
}
save(): Memento {
console.log('Originator: Saving state');
return new ConcreteMemento(this.state);
}
restore(memento: Memento): void {
this.state = memento.getState();
console.log(`Originator: Restoring state to ${this.state}`);
}
}
// Caretaker sınıfı
class Caretaker {
private mementos: Memento[] = [];
addMemento(memento: Memento): void {
this.mementos.push(memento);
}
getMemento(index: number): Memento {
return this.mementos[index];
}
}
// Kullanım
const originator = new Originator();
const caretaker = new Caretaker();
originator.setState('State 1');
caretaker.addMemento(originator.save());
originator.setState('State 2');
caretaker.addMemento(originator.save());
originator.setState('State 3');
console.log('Current State:', originator);
originator.restore(caretaker.getMemento(1));
console.log('Restored State:', originator);
Bu örnekte, Memento arayüzü, nesnenin durumunu temsil eder. ConcreteMemento sınıfı, bu arayüzü uygular ve nesnenin durumunu saklar. Originator sınıfı, durumu ayarlamak, saklamak ve geri yüklemek için kullanılır. Caretaker sınıfı, bir dizi Memento'yu saklar ve gerektiğinde geri yüklemek için kullanılır.
Memento deseni, nesnenin durumunu saklamak ve geri yüklemek için kullanılarak, özellikle kullanıcıların geçmiş durumlarına dönmelerini sağlamak üzere tasarlanmıştır.
State Deseni
"State" (Durum) tasarım deseni, bir nesnenin iç durumunu değiştirdiğinde davranışını da değiştirmesini sağlayan bir davranışsal tasarım desenidir. Bu desen, bir nesnenin durumlarını temsil eden bir dizi sınıf ve bu durumların değiştirilmesini sağlayan bir "Context" (Bağlam) sınıfını içerir.
State deseni, bir nesnenin davranışlarını durumlarına göre değiştirmek için kullanılır. Bu durumlar, genellikle bir arayüzü uygulayan farklı durum sınıfları olarak modellenir. Context sınıfı, iç durumu değiştiren ve bu duruma göre davranışlarını güncelleyen bir arayüz sunar.
İşte TypeScript ile basit bir State deseni örneği:
State
// State arayüzü
interface State {
handleRequest(): void;
}
// ConcreteState sınıfları
class ConcreteStateA implements State {
handleRequest(): void {
console.log('ConcreteStateA is handling the request.');
}
}
class ConcreteStateB implements State {
handleRequest(): void {
console.log('ConcreteStateB is handling the request.');
}
}
// Context sınıfı
class Context {
private state: State;
constructor(state: State) {
this.state = state;
}
setState(state: State): void {
this.state = state;
}
request(): void {
this.state.handleRequest();
}
}
// Kullanım
const initialState = new ConcreteStateA();
const context = new Context(initialState);
context.request(); // ConcreteStateA is handling the request.
const newState = new ConcreteStateB();
context.setState(newState);
context.request(); // ConcreteStateB is handling the request.
Bu örnekte, State arayüzü, durumları temsil eden sınıfların bir arayüzünü tanımlar. ConcreteStateA ve ConcreteStateB sınıfları, bu arayüzü uygular ve farklı durumları temsil eder. Context sınıfı, durumu değiştiren ve bu duruma göre davranışlarını güncelleyen bir sınıftır.
State deseni, bir nesnenin farklı durumlarını temsil eden sınıfları kullanarak, nesnenin davranışlarını bu durumlara göre dinamik olarak değiştirmeyi sağlar. Bu, duruma bağlı olarak farklı davranışlar sergilemek istediğiniz durumlarda kullanışlıdır.
Strategy Deseni
"Strategy" (Strateji) tasarım deseni, bir algoritmanın ailesini tanımlar, bu algoritmaları birer nesne haline getirir ve bu nesnelerin birbirleriyle değiştirilebilir olduğunu sağlar. Yani, bir nesnenin belirli bir davranışını değiştirmek için farklı stratejileri içeren nesneler kullanılır.
Bu desen, bir algoritmanın mantığını içeren sınıfları soyutlar ve bu sınıfları bir arayüz üzerinden erişilebilir kılar. Ardından, bu soyut sınıfları uygulayan farklı sınıflar yazılır ve bu sınıflar, ihtiyaca bağlı olarak değiştirilebilir. Bu durum, bir nesnenin davranışını dinamik olarak değiştirmek istediğinizde kullanışlıdır.
İşte TypeScript ile basit bir Strategy deseni örneği:
Strategy
// Strategy arayüzü
interface Strategy {
execute(): void;
}
// ConcreteStrategy sınıfları
class ConcreteStrategyA implements Strategy {
execute(): void {
console.log('Executing ConcreteStrategyA');
}
}
class ConcreteStrategyB implements Strategy {
execute(): void {
console.log('Executing ConcreteStrategyB');
}
}
// Context sınıfı
class Context {
private strategy: Strategy;
constructor(strategy: Strategy) {
this.strategy = strategy;
}
setStrategy(strategy: Strategy): void {
this.strategy = strategy;
}
executeStrategy(): void {
this.strategy.execute();
}
}
// Kullanım
const strategyA = new ConcreteStrategyA();
const context = new Context(strategyA);
context.executeStrategy(); // Executing ConcreteStrategyA
const strategyB = new ConcreteStrategyB();
context.setStrategy(strategyB);
context.executeStrategy(); // Executing ConcreteStrategyB
Bu örnekte, Strategy arayüzü, stratejileri temsil eden sınıfların bir arayüzünü tanımlar. ConcreteStrategyA ve ConcreteStrategyB sınıfları, bu arayüzü uygular ve farklı stratejileri temsil eder. Context sınıfı, bir stratejiyi içerir ve bu stratejiyi değiştirebilir. executeStrategy metodu, içinde bulunan stratejiyi çağırarak belirli bir davranışı gerçekleştirir.
Strategy deseni, bir nesnenin davranışını değiştirmenin bir yolunu sağlar ve bu durum, özellikle bir nesnenin çeşitli durumlara bağlı olarak farklı davranışlar sergilemesi gerektiğinde kullanışlıdır.
Visitor Deseni
"Visitor" (Ziyaretçi) tasarım deseni, bir nesne üzerinde belirli bir işlemi gerçekleştirmek için bir aile içinde dolaşan ve bu işlemi gerçekleştiren ayrı bir nesneyi temsil eden bir davranışsal tasarım desenidir. Bu desen, nesneler üzerinde yeni işlemler eklemek ve bu işlemleri birbirinden bağımsız bir şekilde gerçekleştirmek için kullanılır.
Visitor deseni, genellikle bir nesne hiyerarşisindeki nesnelerin türlerini temsil eden bir dizi sınıfı içerir. Ayrıca, bu nesnelerin üzerinde farklı işlemleri gerçekleştirmek üzere tasarlanmış bir "Visitor" arayüzü ve bu arayüzü uygulayan "ConcreteVisitor" sınıfları bulunur.
İşte TypeScript ile basit bir Visitor deseni örneği:
Strategy
// Element arayüzü
interface Element {
accept(visitor: Visitor): void;
}
// ConcreteElement sınıfları
class ConcreteElementA implements Element {
accept(visitor: Visitor): void {
visitor.visitElementA(this);
}
}
class ConcreteElementB implements Element {
accept(visitor: Visitor): void {
visitor.visitElementB(this);
}
}
// Visitor arayüzü
interface Visitor {
visitElementA(element: ConcreteElementA): void;
visitElementB(element: ConcreteElementB): void;
}
// ConcreteVisitor sınıfı
class ConcreteVisitor implements Visitor {
visitElementA(element: ConcreteElementA): void {
console.log('ConcreteVisitor visits ConcreteElementA');
}
visitElementB(element: ConcreteElementB): void {
console.log('ConcreteVisitor visits ConcreteElementB');
}
}
// ObjectStructure sınıfı
class ObjectStructure {
private elements: Element[] = [];
addElement(element: Element): void {
this.elements.push(element);
}
accept(visitor: Visitor): void {
this.elements.forEach(element => element.accept(visitor));
}
}
// Kullanım
const objectStructure = new ObjectStructure();
objectStructure.addElement(new ConcreteElementA());
objectStructure.addElement(new ConcreteElementB());
const visitor = new ConcreteVisitor();
objectStructure.accept(visitor);
Bu örnekte, Element arayüzü, ziyaret edilecek nesnelerin ortak bir arayüzünü tanımlar. ConcreteElementA ve ConcreteElementB sınıfları, bu arayüzü uygular ve ziyaretçiye kabul etme işlemini gerçekleştirir. Visitor arayüzü, ziyaretçinin işlemlerini tanımlar. ConcreteVisitor sınıfı, bu arayüzü uygular ve farklı eleman türlerini ziyaret eden işlemleri gerçekleştirir. ObjectStructure sınıfı, ziyaret edilecek nesnelerin koleksiyonunu tutar ve bir ziyaretçi tarafından gezilmesini sağlar.
Visitor deseni, yeni işlemler eklemek ve bu işlemleri nesne hiyerarşisinin değişik sınıflarına uygulamak için kullanılır. Bu sayede, her bir sınıfın kendine özgü bir ziyaretçi ile işlem yapmasına olanak tanır.