JavaRush /Blog Java /Random-MS /"Strategi" Corak Reka Bentuk

"Strategi" Corak Reka Bentuk

Diterbitkan dalam kumpulan
hello! Dalam kuliah sebelum ini, kita telah pun menemui konsep "corak reka bentuk". Sekiranya anda terlupa, izinkan kami mengingatkan anda: istilah ini menandakan penyelesaian standard tertentu kepada masalah biasa dalam pengaturcaraan. Corak reka bentuk "Strategi" - 1Di JavaRush kami sering mengatakan bahawa jawapan kepada hampir semua soalan boleh Googled. Oleh itu, seseorang mungkin telah berjaya menyelesaikan masalah yang serupa dengan anda. Jadi, corak adalah penyelesaian yang diuji masa dan diuji amalan kepada masalah atau kaedah yang paling biasa untuk menyelesaikan situasi masalah. Ini adalah "basikal" yang tidak perlu anda cipta sendiri, tetapi anda perlu tahu bagaimana dan bila menggunakannya :) Satu lagi tugas corak ialah membawa seni bina kepada satu standard. Membaca kod orang lain bukanlah tugas yang mudah! Setiap orang menulisnya secara berbeza, kerana masalah yang sama boleh diselesaikan dalam pelbagai cara. Tetapi penggunaan corak membolehkan pengaturcara yang berbeza memahami logik program tanpa menyelidiki setiap baris kod (walaupun mereka melihatnya buat kali pertama!) Hari ini kita akan melihat salah satu corak yang paling biasa dipanggil "Strategi". Corak reka bentuk "Strategi" - 2Bayangkan kita sedang menulis program yang berfungsi secara aktif dengan objek Kereta. Dalam kes ini, perkara yang sebenarnya dilakukan oleh program kami tidaklah penting. Untuk melakukan ini, kami mencipta sistem warisan dengan satu kelas induk Autodan tiga kelas anak: Sedan, Truckdan F1Car.
public class Auto {

   public void gas() {
       System.out.println("Едем вперед");
   }

   public void stop() {

       System.out.println("Тормозим!");
   }
}

public class Sedan extends Auto {
}

public class Truck extends Auto {
}

public class F1Car extends Auto {
}
Ketiga-tiga kelas kanak-kanak mewarisi dua kaedah standard daripada ibu bapa - gas()dan stop() program kami sangat mudah: kereta hanya boleh memandu ke hadapan dan brek. Meneruskan kerja kami, kami memutuskan untuk menambah kaedah baharu pada kereta - fill()(mengisi minyak). Mari tambahkannya ke kelas induk Auto:
public class Auto {

   public void gas() {
       System.out.println("Едем вперед");
   }

   public void stop() {

       System.out.println("Тормозим!");
   }

   public void fill() {
       System.out.println("Заправить бензин!");
   }
}
Nampaknya masalah boleh timbul dalam situasi yang begitu mudah? Sebenarnya, mereka telah pun bangkit... “Strategi” Corak Reka Bentuk - 3
public class ChildrenBuggies extends Auto {

   public void fill() {

       //хм... Это детский багги, его не надо заправлять :/
   }
}
Sebuah kereta telah muncul dalam program kami yang tidak sesuai dengan konsep umum - kereta kanak-kanak. Ia mungkin berkuasa pedal atau dikawal radio, tetapi satu perkara yang pasti - tidak ada tempat untuk meletakkan petrol ke dalamnya. Skim warisan kami telah menyebabkan kami memberikan kaedah biasa walaupun kepada kelas yang tidak memerlukannya. Apa yang patut kita lakukan dalam keadaan sedemikian? Nah, sebagai contoh, anda boleh mengatasi kaedah fill()dalam kelas ChildrenBuggiessupaya apabila anda cuba mengisi minyak kereta, tiada apa yang berlaku:
public class ChildrenBuggies extends Auto {

   @Override
   public void fill() {
       System.out.println("Игрушечную машину нельзя заправить!");
   }
}
Tetapi penyelesaian ini hampir tidak boleh dipanggil berjaya, sekurang-kurangnya kerana pertindihan kod. Sebagai contoh, kebanyakan kelas akan menggunakan kaedah daripada kelas induk, tetapi kelas lain akan dipaksa untuk mengatasinya. Jika kita mempunyai 15 kelas, dan dalam 5-6 kita dipaksa untuk mengatasi tingkah laku, pertindihan kod akan menjadi agak meluas. Mungkin antara muka boleh membantu kami? Sebagai contoh, yang ini:
public interface Fillable {

   public void fill();
}
Kami akan mencipta antara muka Fillabledengan satu kaedah fill(). Sehubungan itu, kereta yang perlu mengisi minyak akan melaksanakan antara muka ini, tetapi kereta lain (contohnya, kereta kami) tidak akan melaksanakannya. Tetapi pilihan ini juga tidak sesuai dengan kami. Hierarki kelas kami mungkin berkembang kepada jumlah yang sangat besar pada masa hadapan (bayangkan berapa banyak jenis kereta yang terdapat di dunia). Kami meninggalkan pilihan warisan sebelumnya kerana kami tidak mahu membatalkan fill(). Di sini kita perlu melaksanakannya dalam setiap kelas! Bagaimana jika kita mempunyai 50 daripadanya? Dan jika perubahan kerap dibuat pada program kami (dan dalam program sebenar ini hampir selalu berlaku!), Kami perlu berlari-lari dengan lidah kami melepak antara semua 50 kelas dan menukar tingkah laku setiap kelas secara manual. Jadi apa yang harus kita lakukan pada akhirnya? Untuk menyelesaikan masalah kita, mari kita pilih jalan yang berbeza. Iaitu, mari kita asingkan tingkah laku kelas kita daripada kelas itu sendiri. Apakah maksudnya? Seperti yang anda ketahui, mana-mana objek mempunyai keadaan (satu set data) dan tingkah laku (satu set kaedah). Tingkah laku kelas mesin kami terdiri daripada tiga kaedah - gas(), stop()dan fill(). Dua kaedah pertama adalah baik. Tetapi kami akan memindahkan kaedah ketiga di luar kelas Auto. Ini akan menjadi pemisahan tingkah laku daripada kelas (lebih tepat lagi, kita hanya memisahkan sebahagian daripada tingkah laku - dua kaedah pertama kekal di tempatnya). Di manakah kita harus mengalihkan kaedah kita fill()? Tiada apa-apa yang langsung terlintas di fikiran :/ Dia seolah-olah berada di tempatnya sepenuhnya. Kami akan mengalihkannya ke antara muka yang berasingan - FillStrategy!
public interface FillStrategy {

   public void fill();
}
Mengapa kita memerlukan antara muka ini? Mudah sahaja. Sekarang kita boleh membuat beberapa kelas yang akan melaksanakan antara muka ini:
public class HybridFillStrategy implements FillStrategy {

   @Override
   public void fill() {
       System.out.println("Заправляем бензином or электричеством на выбор!");
   }
}

public class F1PitstopStrategy implements FillStrategy {

   @Override
   public void fill() {
       System.out.println("Заправляем бензин только после всех остальных procedures пит-стопа!");
   }
}

public class StandartFillStrategy implements FillStrategy {
   @Override
   public void fill() {
       System.out.println("Просто заправляем бензин!");
   }
}
Kami telah mencipta tiga strategi tingkah laku - untuk kereta konvensional, untuk hibrid dan untuk kereta Formula 1. Setiap strategi melaksanakan algoritma pengisian bahan api yang berasingan. Dalam kes kami, ini hanyalah output kepada konsol, tetapi mungkin terdapat beberapa logik kompleks di dalam kaedah tersebut. Apa yang perlu kita lakukan dengan ini seterusnya?
public class Auto {

   FillStrategy fillStrategy;

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

   public void gas() {
       System.out.println("Едем вперед");
   }

   public void stop() {
       System.out.println("Тормозим!");
   }

}
Kami menggunakan antara muka kami FillStrategysebagai medan dalam kelas induk Auto. Sila ambil perhatian: kami tidak menyatakan pelaksanaan tertentu, sebaliknya menggunakan antara muka. Dan kami memerlukan pelaksanaan khusus antara muka FillStrategydalam kelas kereta kanak-kanak:
public class F1Car extends Auto {

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

public class HybridAuto extends Auto {

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

public class Sedan extends Auto {

   public Sedan() {
       this.fillStrategy = new StandartFillStrategy();
   }
}
Mari lihat apa yang kami dapat:
public class Main {

   public static void main(String[] args) {

       Auto sedan = new Sedan();
       Auto hybrid = new HybridAuto();
       Auto f1car = new F1Car();

       sedan.fill();
       hybrid.fill();
       f1car.fill();
   }
}
Output konsol:

Просто заправляем бензин!
Заправляем бензином or электричеством на выбор!
Заправляем бензин только после всех остальных procedures пит-стопа!
Hebat, proses mengisi minyak berfungsi sebagaimana mestinya! Dengan cara ini, tiada apa yang menghalang kami daripada menggunakan strategi sebagai parameter dalam pembina! Sebagai contoh, seperti ini:
public class Auto {

   private FillStrategy fillStrategy;

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

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

   public void gas() {
       System.out.println("Едем вперед");
   }

   public void stop() {
       System.out.println("Тормозим!");
   }
}

public class Sedan extends Auto {

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



public class HybridAuto extends Auto {

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

public class F1Car extends Auto {

   public F1Car() {
       super(new F1PitstopStrategy());
   }
}
Mari jalankan kaedah kami main()(ia kekal tidak berubah) dan dapatkan hasil yang sama! Output konsol:

Просто заправляем бензин!
Заправляем бензином or электричеством на выбор!
Заправляем бензин только после всех остальных procedures пит-стопа!
Corak Strategi mentakrifkan keluarga algoritma, merangkum setiap satu daripadanya dan memastikan bahawa ia boleh ditukar ganti. Ia membolehkan anda mengubah suai algoritma tanpa mengira penggunaannya di sisi pelanggan (takrifan ini diambil daripada buku "Menjelajahi Corak Reka Bentuk" dan nampaknya saya sangat berjaya). “Strategi” Corak Reka Bentuk - 4Kami telah mengasingkan keluarga algoritma yang menarik minat kami (jenis kereta mengisi minyak) ke dalam antara muka yang berasingan dengan beberapa pelaksanaan. Kami telah memisahkan mereka daripada intipati kereta. Oleh itu, sekarang, jika kita perlu membuat sebarang perubahan pada proses mengisi minyak ini atau itu, ini tidak akan menjejaskan kelas kereta kita dalam apa cara sekalipun. Bagi kebolehtukaran, untuk mencapainya, kami hanya perlu menambah satu kaedah penetap ke kelas kami Auto:
public class Auto {

   FillStrategy fillStrategy;

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

   public void gas() {
       System.out.println("Едем вперед");
   }

   public void stop() {
       System.out.println("Тормозим!");
   }

   public void setFillStrategy(FillStrategy fillStrategy) {
       this.fillStrategy = fillStrategy;
   }
}
Kini kita boleh menukar strategi dengan cepat:
public class Main {

   public static void main(String[] args) {

       ChildrenBuggies buggies = new ChildrenBuggies();
       buggies.setFillStrategy(new StandartFillStrategy());

       buggies.fill();
   }
}
Jika tiba-tiba kereta buggy kanak-kanak mula diisi dengan petrol, program kami akan bersedia untuk senario seperti itu :) Itu sahaja, sebenarnya! Anda telah mempelajari corak reka bentuk lain, yang sudah pasti anda perlukan dan akan membantu anda lebih daripada sekali apabila mengerjakan projek sebenar :) Jumpa lagi!
Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION