Adapter Pattern

Structural yani yapısal veya yapıların kompozisyonuna dayalı olarak ilk inceleyeceğimiz tasarım kalıbı adapter pattern olacaktır. Bu kalıp birbirinden bağımsız iki ayrı yapının kullanımını kombine etmek üzerine kurulmuştur. İsminden de anlaşılacağı üzere, entegrasyon için birbirine uymayan iki yapının birbirine “adapte” edilmesi bu kalıbın ana hedefidir.

Geçtiğimiz yıllarda Apple şirketi radikal bir karar ile telefonlarından standart 3.5 mm kulaklık girişini kaldırmıştı. Yoğun tepki çeken bu yenilik yanında bir de çözüm ile gelmişti. Zira Apple eski kulaklıklarını kullanmaya devam etmek isteyen kişiler için bir dönüştürücü parça satmaya başlamıştı. Bu parçanın görevi eski tipteki kulaklıkların, yeni bağlantı birimine uyumlanması yani adapte edilmesiydi.

Bu tarz problemler yazılımda da karşımıza çıkar. Bir kütüphanenin, sınıfın, methodun girdi ve çıktıları istendiği gibi olmayabilir. Bu durumda bu sınıfı sarmalayacak ya da sektör dili ile “wrap edecek” çözümler getiririz. Adapter patternin devreye girdiği yer tam olarak burasıdır. Adapter kalıbı tasarımı bitmiş iki ayrı yapının entegrasyonu için kullanılmaktadır. Bu cümlenin altını çizmek isterim. Zira tasarımı sonlanmamış iki yapının entegrasyonunda çözüm için alternatiflerimiz hala mevcuttur.

Bu tasarım kalıbımızın kullanımı görebilmek için örnek bir kod yazalım. Varsayalım ki bir servisimiz var ve bu servis istenen cevabı bir ArrayList şeklinde dönüyor. Öncelikle orijinal durumun kodunu paylaşmak istiyorum.

import java.util.List;

public interface Title {
	
	public List<String> getTitleYears();

}
import java.util.ArrayList;
import java.util.List;

public class TitleImpl implements Title {

	@Override
	public List<String> getTitleYears() {
		
		System.out.println("I'm TitleImpl and I return List<String>");
		
		List<String> titleYears = new ArrayList();
		
		titleYears.add("1994");
		titleYears.add("1995");
		titleYears.add("2000");
		titleYears.add("2001");
		titleYears.add("2002");
		titleYears.add("2003");
		titleYears.add("2004");
		
		return titleYears;
	}
	
}
import java.util.List;

public class StatsService {

	public static void main(String[] args) {

		System.out.println(getStatsByTitleImpl());
		
	}

	public static List<String> getStatsByTitleImpl() {

		TitleImpl titleImpl = new TitleImpl();

		return titleImpl.getTitleYears();

	}
	
}
Çıktı:
I'm TitleImpl and I return List<String>
[1994, 1995, 2000, 2001, 2002, 2003, 2004]

Buradaki Title arayüzünün implementasyonu olan TitleImpl sınıfı çıktıyı üretmektedir. Kodlama olarak basit gözüken bu durumu, yine basit bir kod parçasıyla bir probleme çevirelim. Varsayalım ki TitleImpl sınıfı, ilettiği sonucu, bir web servis çağrısı ile hesaplıyor olsun. Şirketinizin bir dönüşüme gittiğini ve web servis yapılarının değiştiğini hayal edin. Bu servis entegrasyon noktasında tamamen farklı bir arayüz ve implementasyonu ile geliyor olsun.

public interface Value {

	public String getValuesAsDashSeperated();	
	
}
public class ValueImpl implements Value {

	@Override
	public String getValuesAsDashSeperated() {
		
		System.out.println("I'm ValueImpl and I return String");
		
		return "1994-1995-2000-2001-2002-2003-2004";

	}

}

Aradaki farka baktığımızda method imzası değişmiş ve buna bağlı olarak artık ArrayList yerine metin değer dönen bir servis geldiğini hayal edelim. Bu methodu çalıştırdığımızda tamamen farklı bir çıktı elde ederiz.

Çıktı:
I'm ValueImpl and I return String
1994-1995-2000-2001-2002-2003-2004

Bu iki servis birbirinden tamamen farklıdır. Ancak uygulamamızın ana akışı TitleImpl ile yazılmıştır. Hayal edeceğiniz üzere ValueImpl ile erlerini değiştirmek oldukça büyük etkilere yol açabilir. İşte bu durumda ValueImpl sınıfını TitleImpl gibi kullanabilmemiz için bir çeviriciye ihtiyaç vardır. Bu sınıf Value arayüzünden bir instance almalı ve Title arayüzünün implementasyonu olmalıdır. Böylece iki yapının kompozisyonu ile karşılaşırız.

import java.util.Arrays;
import java.util.List;

public class ValueAdapter extends TitleImpl {

	private Value value;
	
	public ValueAdapter(Value value) {
		this.value = value;
	}
	
	@Override
	public List<String> getTitleYears() {
		
		System.out.println("I'm ValueAdapter and I return List<String>");
		
		List<String> titleYears = Arrays.asList(value.getValuesAsDashSeperated().split("-"));
		
		return titleYears;
	}
	
}

Buradaki örneğin daha iyi anlaşılması için 3 ayrı durumu da main methodu içerisinden çağırıp, hem akışı hem de farklarını görelim. Ayrıca bu çıktının altında yaptıklarımızı görselleştiren bir de çizim paylaşacağım.

import java.util.List;

public class StatsService {

	public static void main(String[] args) {

		System.out.println("----getStatsByTitleImpl()----");
		System.out.println(getStatsByTitleImpl());
		
		System.out.println("\n\n----getStatsByValueImpl()----");
		System.out.println(getStatsByValueImpl());
		
		System.out.println("\n\n----getTitleStatsByAdapter()----");
		System.out.println(getTitleStatsByAdapter());
		
	}

	public static List<String> getStatsByTitleImpl() {

		TitleImpl titleImpl = new TitleImpl();

		return titleImpl.getTitleYears();

	}
	
	public static String getStatsByValueImpl() {

		ValueImpl valueImpl = new ValueImpl();

		return valueImpl.getValuesAsDashSeperated();

	}
	
	public static List<String> getTitleStatsByAdapter() {

		ValueImpl valueImpl = new ValueImpl();
		
		ValueAdapter valueAdapter = new ValueAdapter(valueImpl);

		return valueAdapter.getTitleYears();

	}

}
Çıktı:
----getStatsByTitleImpl()----
I'm TitleImpl and I return List<String>
[1994, 1995, 2000, 2001, 2002, 2003, 2004]


----getStatsByValueImpl()----
I'm ValueImpl and I return String
1994-1995-2000-2001-2002-2003-2004


----getTitleStatsByAdapter()----
I'm ValueAdapter and I return List<String>
I'm ValueImpl and I return String
[1994, 1995, 2000, 2001, 2002, 2003, 2004]

Yapısal kalıplarımızdan olan adapter tasarım kalıbı, özellikle kullanmak istediğimiz bir komponentin mevcut uygulamamıza uygun durumda olmaması durumunda kullanabileceğimiz bir çözümdür. Orijinal kod içerisinde değişiklik yapmadan yeni yapıyı kullanabilir durumda oluruz. Bu da çevrim veya adaptasyon yeteneğinin iş mantığından ayrılması noktasında bize yardımcı olur. Öte yandan ayrışan bu mantığı barındıran sınıfların uygulamaya eklenmesi gerekir ve bu da uygulamadaki sınıf sayısının artmasına yol açacaktır. Bir başka bakış açısıyla; open/closed prensibine hizmet eder ve mevcut uygulamalara, yeni komponentlerin entegrasyonu noktasında yaygın kullanımı söz konusudur.

Leave a Reply

Your email address will not be published. Required fields are marked *