Singleton nesne kullanımını zaten inceledik, ancak bu stratejinin bir tasarım modeli olduğunu ve bu konuda en çok kullanılanlardan biri olduğunu henüz fark etmemiş olabilirsiniz.

Aslında, bu kalıplardan pek çok var ve bunlar özel amaçlarına göre sınıflandırılabilirler.

Desen sınıflandırması

Desen Tipi Başvuru
yaratıcı Nesne oluşturma problemini çözen bir tür
Yapısal Mimarimizde doğru ve genişletilebilir bir sınıf hiyerarşisi oluşturmamızı sağlayan kalıplar
Davranışsal Bu kalıp kümesi, bir programdaki nesneler arasında güvenli ve uygun etkileşimi kolaylaştırır.

Tipik olarak, bir model çözdüğü problemle karakterize edilir. Java ile çalışırken en sık karşılaştığımız birkaç kalıba bir göz atalım:

Model Amaç
Tekil Bu kalıba zaten aşinayız - birden fazla örneğe sahip olamayacak bir sınıf oluşturmak ve bu sınıfa erişmek için kullanıyoruz.
Yineleyici Biz de buna aşinayız. Bu kalıbın, bir koleksiyon nesnesinin iç temsilini açığa çıkarmadan yinelememize izin verdiğini biliyoruz. Koleksiyonlarla birlikte kullanılır.
Adaptör Bu kalıp, uyumsuz nesneleri birlikte çalışabilmeleri için birbirine bağlar. Bağdaştırıcı modelinin adının, bunun tam olarak ne yaptığını hayal etmenize yardımcı olduğunu düşünüyorum. İşte gerçek hayattan basit bir örnek: duvar prizi için bir USB adaptörü.
Şablon yöntemi

Entegrasyon problemini çözen ve bir algoritmanın yapısını değiştirmeden algoritmik adımları değiştirmenize izin veren bir davranışsal programlama modeli.

Bir dizi montaj adımı şeklinde bir araba montaj algoritmasına sahip olduğumuzu hayal edin:

Şasi -> Gövde -> Motor -> Kabin İçi

Güçlendirilmiş bir çerçeve, daha güçlü bir motor veya ek aydınlatmalı bir iç mekan koyarsak, algoritmayı değiştirmemiz gerekmez ve soyut sıralama aynı kalır.

Dekoratör Bu model, nesnelere yararlı işlevsellik kazandırmak için sarmalayıcılar oluşturur. Bunu bu makalenin bir parçası olarak ele alacağız.

Java.io'da aşağıdaki sınıflar kalıpları uygular:

Model Java.io'da nerede kullanılır?
Adaptör
Şablon yöntemi
Dekoratör

dekoratör deseni

Bir ev tasarımı için bir model tarif ettiğimizi düşünelim.

Genel olarak, yaklaşım şöyle görünür:

Başlangıçta, birkaç ev tipi seçeneğimiz var. Minimum konfigürasyon, çatılı bir kattır. Ardından, evin fiyatını doğal olarak etkileyen ek parametreleri değiştirmek için her türlü dekoratörü kullanıyoruz.

Soyut bir House sınıfı yaratıyoruz:

public abstract class House {
	String info;

	public String getInfo() {
    	return info;
	}

	public abstract int getPrice();
}

Burada 2 yöntemimiz var:

  • getInfo() evimizin adı ve özellikleri hakkında bilgi verir;
  • getPrice(), geçerli ev yapılandırmasının fiyatını döndürür.

Ayrıca standart Ev uygulamalarımız da var — tuğla ve ahşap:

public class BrickHouse extends House {

	public BrickHouse() {
    	info = "Brick House";
	}

	@Override
	public int getPrice() {
    	return 20_000;
	}
}

public class WoodenHouse extends House {

	public WoodenHouse() {
    	info = "Wooden House";
	}

	@Override
	public int getPrice() {
    	return 25_000;
	}
}

Her iki sınıf da Ev sınıfını devralır ve fiyat yöntemini geçersiz kılarak standart bir ev için özel bir fiyat belirler. Yapıcıda adı belirledik.

Ardından, dekoratör sınıfları yazmamız gerekiyor. Bu sınıflar aynı zamanda House sınıfını da devralır . Bunu yapmak için soyut bir dekoratör sınıfı oluşturuyoruz.

Bir nesneyi değiştirmek için ek mantık koyacağımız yer burasıdır. Başlangıçta ek bir mantık olmayacak ve soyut sınıf boş olacaktır.

abstract class HouseDecorator extends House {
}

Daha sonra dekoratör uygulamalarını oluşturuyoruz. Eve ek özellikler eklememize izin veren birkaç sınıf oluşturacağız:

public class SecondFloor extends HouseDecorator {
	House house;

	public SecondFloor(House house) {
    	this.house = house;
	}

	@Override
	public int getPrice() {
    	return house.getPrice() + 20_000;
	}

	@Override
	public String getInfo() {
    	return house.getInfo() + " + second floor";
	}
}
Evimize ikinci bir kat ekleyen bir dekoratör

Dekoratör yapıcı, "süsleyeceğimiz", yani değişiklikler ekleyeceğimiz bir evi kabul eder. Ve getPrice() ve getInfo() yöntemlerini geçersiz kılarak , eski evi temel alarak güncellenen yeni ev hakkında bilgi döndürürüz.

public class Garage extends HouseDecorator {

	House house;
	public Garage(House house) {
    	this.house = house;
	}

	@Override
	public int getPrice() {
    	return house.getPrice() + 5_000;
	}

	@Override
	public String getInfo() {
    	return house.getInfo() + " + garage";
	}
}
Evimize bir garaj ekleyen bir dekoratör

Artık evimizi dekoratörlerle güncelleyebiliriz. Bunu yapmak için bir ev yaratmamız gerekiyor:

House brickHouse = new BrickHouse();

Ardından,evevimize geçen yeni bir dekoratöre eşit değişken:

brickHouse = new SecondFloor(brickHouse);

Bizimevdeğişken şimdi ikinci katı olan bir ev.

Dekoratörleri içeren kullanım durumlarına bakalım:

Örnek kod Çıktı
House brickHouse = new BrickHouse();

  System.out.println(brickHouse.getInfo());
  System.out.println(brickHouse.getPrice());

Tuğla ev

20000

House brickHouse = new BrickHouse();

  brickHouse = new SecondFloor(brickHouse);

  System.out.println(brickHouse.getInfo());
  System.out.println(brickHouse.getPrice());

Tuğla Ev + ikinci kat

40000

House brickHouse = new BrickHouse();


  brickHouse = new SecondFloor(brickHouse);
  brickHouse = new Garage(brickHouse);

  System.out.println(brickHouse.getInfo());
  System.out.println(brickHouse.getPrice());

Tuğla Ev + ikinci kat + garaj

45000

House woodenHouse = new SecondFloor(new Garage(new WoodenHouse()));

  System.out.println(woodenHouse.getInfo());
  System.out.println(woodenHouse.getPrice());

Ahşap Ev + garaj + ikinci kat

50000

House woodenHouse = new WoodenHouse();

  House woodenHouseWithGarage = new Garage(woodenHouse);

  System.out.println(woodenHouse.getInfo());
  System.out.println(woodenHouse.getPrice());

  System.out.println(woodenHouseWithGarage.getInfo());
  System.out.println(woodenHouseWithGarage.getPrice());

Ahşap ev

25000

Ahşap Ev + garaj

30000

Bu örnek, bir nesneyi bir dekoratörle yükseltmenin faydasını göstermektedir. Yani değiştirmedikAhşap evnesnenin kendisi, ancak bunun yerine eskisini temel alan yeni bir nesne yarattı. Burada avantajların dezavantajlarla birlikte geldiğini görebiliriz: her seferinde bellekte yeni bir nesne yaratarak bellek tüketimini artırırız.

Programımızın şu UML diyagramına bakın:

Bir dekoratörün süper basit bir uygulaması vardır ve nesneleri dinamik olarak değiştirerek onları yükseltir. Dekoratörler, geçerli sınıfla aynı soyut türdeki veya arabirimdeki nesneleri parametre olarak alan yapıcıları tarafından tanınabilir. Java'da bu kalıp, G/Ç sınıflarında yaygın olarak kullanılır.

Örneğin, daha önce belirttiğimiz gibi, java.io.InputStream , OutputStream , Reader ve Writer'ın tüm alt sınıfları , aynı sınıfların nesnelerini kabul eden bir kurucuya sahiptir.