CodeGym /Java Blogu /Rastgele /Strateji Tasarım Modeli
John Squirrels
Seviye
San Francisco

Strateji Tasarım Modeli

grupta yayınlandı
MERHABA! Bugünün dersinde, Strateji kalıbı hakkında konuşacağız. Önceki derslerde, kalıtım kavramıyla kısaca tanışmıştık. Unuttuysanız, bu terimin ortak bir programlama görevine yönelik standart bir çözümü ifade ettiğini size hatırlatacağım. CodeGym'de, hemen hemen her sorunun cevabını google'da bulabileceğinizi sık sık söyleriz. Bunun nedeni, görevinizin, her ne ise, muhtemelen zaten başka biri tarafından başarılı bir şekilde çözülmüş olmasıdır. Kalıplar, en yaygın görevlere denenmiş ve doğru çözümler veya sorunlu durumları çözmeye yönelik yöntemlerdir. Bunlar "tekerlekler" gibidir, kendi başınıza yeniden icat etmenize gerek yoktur, ancak bunları nasıl ve ne zaman kullanacağınızı bilmeniz gerekir :) Kalıpların bir başka amacı da tek tip mimariyi teşvik etmektir. Başkasının kodunu okumak kolay bir iş değildir! Herkes farklı kod yazar, çünkü aynı görev birçok şekilde çözülebilir. Ancak kalıpların kullanılması, farklı programcıların programlama mantığını her bir kod satırına girmeden (ilk defa görüyor olsalar bile!) anlamalarına yardımcı olur. Bugün "Strateji" adı verilen en yaygın tasarım modellerinden birine bakıyoruz. Tasarım deseni: Strateji - 2İletim nesneleri ile aktif olarak çalışacak bir program yazdığımızı hayal edin. Programımızın tam olarak ne yaptığı önemli değil. Bir Conveyance üst sınıfı ve üç alt sınıfı olan bir sınıf hiyerarşisi oluşturduk : Sedan , Truck ve F1Car .

public class Conveyance {

   public void go() {
       System.out.println("Moving forward");
   }

   public void stop() {

       System.out.println("Braking!");
   }
}

public class Sedan extends Conveyance {
}

public class Truck extends Conveyance {
}

public class F1Car extends Conveyance {
}
Üç alt sınıfın tümü, ebeveynden iki standart yöntemi miras alır: go() ve stop() . Programımız çok basit: Arabalarımız sadece ilerleyip fren yapabiliyor. Çalışmamıza devam ederek arabalara yeni bir yöntem vermeye karar verdik: fill() ("benzin deposunu doldurun" anlamına gelir). Bunu Conveyance üst sınıfına ekledik :

public class Conveyance {

   public void go() {
       System.out.println("Moving forward");
   }

   public void stop() {

       System.out.println("Braking!");
   }
  
   public void fill() {
       System.out.println("Refueling!");
   }
}
Sorunlar gerçekten bu kadar basit bir durumda ortaya çıkabilir mi? Aslında onlar zaten... Tasarım deseni: Strateji - 3

public class Stroller extends Conveyance {

   public void fill() {
      
       // Hmm... This is a stroller for children. It doesn't need to be refueled :/
   }
}
Programımızda artık genel konsepte pek uymayan bir taşıt (bebek arabası) var. Pedalları olabilir veya radyo kontrollü olabilir, ancak kesin olan bir şey var - benzin koyacak yeri olmayacak. Sınıf hiyerarşimiz, ortak yöntemlerin onlara ihtiyaç duymayan sınıflar tarafından miras alınmasına neden oldu. Bu durumda ne yapmalıyız? Pekala, Stroller sınıfındaki fill() yöntemini geçersiz kılabiliriz, böylece bebek arabasına yakıt ikmali yapmaya çalıştığınızda hiçbir şey olmaz:

public class Stroller extends Conveyance {

   @Override
   public void fill() {
       System.out.println("A stroller cannot be refueled!");
   }
}
Ancak, yinelenen koddan başka bir neden yoksa, buna başarılı bir çözüm denemez. Örneğin, sınıfların çoğu üst sınıfın yöntemini kullanacak, ancak geri kalanı onu geçersiz kılmak zorunda kalacak. 15 sınıfımız varsa ve bunların 5-6 tanesinde davranışı geçersiz kılmamız gerekirse, kod çoğaltma oldukça kapsamlı hale gelecektir. Belki arayüzler bize yardımcı olabilir? Örneğin, bunun gibi:

public interface Fillable {
  
   public void fill();
}
Bir fill() yöntemiyle bir Fillable arabirimi oluşturacağız . Ardından, yakıt ikmali yapılması gereken araçlar bu arayüzü uygularken diğer araçlar (örneğin, bebek arabamız) uygulamaz. Ancak bu seçenek bize uymuyor. Gelecekte, sınıf hiyerarşimiz büyüyerek çok büyüyebilir (dünyada kaç farklı taşıt türü olduğunu hayal edin). Kalıtımı içeren önceki sürümü bıraktık, çünkü fill() işlevini geçersiz kılmak istemiyoruz.yöntem birçok kez. Şimdi bunu her sınıfta uygulamak zorundayız! Peki ya 50'miz varsa? Ve programımızda sık sık değişiklikler yapılacaksa (ve bu neredeyse her zaman gerçek programlar için geçerlidir!), 50 sınıfın hepsini aceleyle geçmemiz ve her birinin davranışını manuel olarak değiştirmemiz gerekir. Peki, sonunda, bu durumda ne yapmalıyız? Sorunumuzu çözmek için farklı bir yol seçeceğiz. Yani, sınıfımızın davranışını sınıfın kendisinden ayıracağız. Bu ne anlama gelir? Bildiğiniz gibi, her nesnenin durumu (bir dizi veri) ve davranışı (bir dizi yöntem) vardır. Taşıma sınıfımızın davranışı üç yöntemden oluşur: go() , stop() ve fill() . İlk iki yöntem olduğu gibi iyidir. Ancak üçüncü yöntemi devre dışı bırakacağız.Taşıma sınıfı. Bu, davranışı sınıftan ayıracaktır (daha doğrusu, davranışın yalnızca bir kısmını ayıracaktır, çünkü ilk iki yöntem oldukları yerde kalacaktır). Peki fill() metodumuzu nereye koymalıyız ? Aklıma hiçbir şey gelmiyor :/ Tam olması gerektiği yerde gibi. Onu ayrı bir arayüze taşıyacağız: FillStrategy !

public interface FillStrategy {

   public void fill();
}
Neden böyle bir arayüze ihtiyacımız var? Her şey basit. Artık bu arayüzü uygulayan birkaç sınıf oluşturabiliriz:

public class HybridFillStrategy implements FillStrategy {
  
   @Override
   public void fill() {
       System.out.println("Refuel with gas or electricity — your choice!");
   }
}

public class F1PitstopStrategy implements FillStrategy {
  
   @Override
   public void fill() {
       System.out.println("Refuel with gas only after all other pit stop procedures are complete!");
   }
}

public class StandardFillStrategy implements FillStrategy {
   @Override
   public void fill() {
       System.out.println("Just refuel with gas!");
   }
}
Üç davranış stratejisi oluşturduk: biri sıradan arabalar için, biri hibrit araçlar için ve biri de Formula 1 yarış arabaları için. Her strateji farklı bir yakıt ikmali algoritması uygular. Bizim durumumuzda, konsolda basitçe bir dizi gösteriyoruz, ancak her yöntem bazı karmaşık mantıklar içerebilir. Sonra ne yapıyoruz?

public class Conveyance {

   FillStrategy fillStrategy;

   public void fill() {
       fillStrategy.fill();
   }

   public void go() {
       System.out.println("Moving forward");
   }

   public void stop() {
       System.out.println("Braking!");
   }
  
}
FillStrategy arayüzümüzü Conveyance üst sınıfında bir alan olarak kullanıyoruz . Belirli bir uygulamayı belirtmediğimize dikkat edin - bir arayüz kullanıyoruz. Araba sınıfları, FillStrategy arayüzünün belirli uygulamalarına ihtiyaç duyacaktır:

public class F1Car extends Conveyance {

   public F1Car() {
       this.fillStrategy = new F1PitstopStrategy();
   }
}

public class HybridCar extends Conveyance {

   public HybridCar() {
       this.fillStrategy = new HybridFillStrategy();
   }
}

public class Sedan extends Conveyance {

   public Sedan() {
       this.fillStrategy = new StandardFillStrategy();
   }
}

Neye sahip olduğumuza bir bakalım!

public class Main {

   public static void main(String[] args) {

       Conveyance sedan = new Sedan();
       Conveyance hybrid = new HybridCar();
       Conveyance f1car = new F1Car();

       sedan.fill();
       hybrid.fill();
       f1car.fill();
   }
}
Konsol çıktısı:

Just refuel with gas! 
Refuel with gas or electricity — your choice! 
Refuel with gas only after all other pit stop procedures are complete!
Harika! Yakıt ikmali süreci olması gerektiği gibi çalışıyor! Bu arada, stratejiyi yapıcıda bir parametre olarak kullanmamızı hiçbir şey engellemiyor! Örneğin, bunun gibi:

public class Conveyance {

   private FillStrategy fillStrategy;

   public Conveyance(FillStrategy fillStrategy) {
       this.fillStrategy = fillStrategy;
   }

   public void fill() {
       this.fillStrategy.fill();
   }

   public void go() {
       System.out.println("Moving forward");
   }

   public void stop() {
       System.out.println("Braking!");
   }
}

public class Sedan extends Conveyance {

   public Sedan() {
       super(new StandardFillStrategy());
   }
}



public class HybridCar extends Conveyance {

   public HybridCar() {
       super(new HybridFillStrategy());
   }
}

public class F1Car extends Conveyance {

   public F1Car() {
       super(new F1PitstopStrategy());
   }
}
Main() yöntemimizi çalıştıralım (değişmeden kalır). Aynı sonucu alıyoruz! Konsol çıktısı:

Just refuel with gas! 
Refuel with gas or electricity — your choice! 
Refuel with gas only after all other pit stop procedures are complete!
Strateji tasarım modeli, bir algoritma ailesini tanımlar, her birini kapsar ve birbirlerinin yerine kullanılabilir olmalarını sağlar. Müşteri tarafından nasıl kullanıldıklarına bakılmaksızın algoritmaları değiştirmenize izin verir ("Head First Design Patterns" kitabından alınan bu tanım bana mükemmel görünüyor). Tasarım deseni: Strateji - 4İlgilendiğimiz algoritma ailesini (arabalara yakıt ikmali yapma yolları) farklı uygulamalara sahip ayrı arayüzlerde zaten belirtmiştik. Onları arabanın kendisinden ayırdık. Şimdi, belirli bir yakıt ikmali algoritmasında herhangi bir değişiklik yapmamız gerekirse, bu, araba sınıflarımızı hiçbir şekilde etkilemeyecektir. Değiştirilebilirliği sağlamak için, Conveyance sınıfımıza tek bir ayarlayıcı yöntemi eklememiz yeterlidir :

public class Conveyance {

   FillStrategy fillStrategy;

   public void fill() {
       fillStrategy.fill();
   }

   public void go() {
       System.out.println("Moving forward");
   }

   public void stop() {
       System.out.println("Braking!");
   }

   public void setFillStrategy(FillStrategy fillStrategy) {
       this.fillStrategy = fillStrategy;
   }
}
Artık stratejileri anında değiştirebiliriz:

public class Main {

   public static void main(String[] args) {

       Stroller stroller= new Stroller();
       stroller.setFillStrategy(new StandardFillStrategy());

       stroller.fill();
   }
}
Bebek arabaları aniden benzinle çalışmaya başlarsa, programımız bu senaryoyu işlemek için hazır olacaktır :) İşte bu kadar! Gerçek projeler üzerinde çalışırken şüphesiz gerekli ve yardımcı olacak bir tasarım modeli daha öğrendiniz :) Bir dahaki sefere görüşmek üzere!
Yorumlar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION