JavaRush /Java блогу /Random-KY /Дизайн үлгүсү "Стратегия"

Дизайн үлгүсү "Стратегия"

Группада жарыяланган
Салам! Мурунку лекцияларда биз «дизайн үлгүсү» сыяктуу түшүнүккө жолукканбыз. Эгер унутуп калган болсоңуз, эсиңизге салалы: бул термин программалоодогу жалпы маселенин белгилүү бир стандарттуу чечимин билдирет. Дизайн үлгүсү "Стратегия" - 1JavaRushта биз көп айтабыз, дээрлик бардык суроолорго жоопту Google'дан тапса болот. Демек, кимдир бирөө сиздикине окшош көйгөйдү ийгorктүү чечкен болушу мүмкүн. Ошентип, үлгүлөр - бул эң кеңири таралган көйгөйлөрдүн же көйгөйлүү кырдаалдарды чечүүнүн ыкмаларынын убакыт жана практикалык сыноодон өткөн чечимдери. Булар эң эле "велосипеддер", аларды эч кандай учурда өзүңүз ойлоп табуунун кереги жок, бирок сиз аларды кантип жана качан колдонууну бorшиңиз керек :) Үлгүлөрдүн дагы бир милдети - архитектураны бирдиктүү стандартка жеткирүү. Башка бирөөнүн codeун окуу оңой иш эмес! Аны ар ким ар кандай жазат, анткени бир эле маселени ар кандай жолдор менен чечсе болот. Бирок калыптарды колдонуу ар кандай программисттерге программанын логикасын ар бир codeдун саптарына тереңдеп кирбестен түшүнүүгө мүмкүндүк берет (алар аны биринчи жолу көрүшсө да!) Бүгүн биз “Стратегия” деп аталган эң кеңири таралган моделдердин бирин карап чыгабыз. Дизайн үлгүсү "Стратегия" - 2Биз Car an objectи менен активдүү иштеген программаны жазып жатабыз деп элестетип көрөлү. Бул учурда, биздин программанын эмне кылганы өзгөчө маанилүү эмес. Бул үчүн биз бир ата-эне классы Autoжана үч бала классы бар мурас тутумун түздүк: Sedan, Truckжана 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 {
}
Балдардын үч классы тең ата-энеден эки стандарттуу ыкманы мурастайт – gas()жана stop() биздин программа абдан жөнөкөй: унаалар алдыга гана айдап, тормоздой алат. Ишибизди улантып, биз унааларга жаңы ыкманы - fill()(май куюу) кошууну чечтик. Аны ата-эне классына кошолу Auto:
public class Auto {

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

   public void stop() {

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

   public void fill() {
       System.out.println("Заправить бензин!");
   }
}
Мындай жөнөкөй кырдаалда көйгөйлөр пайда болушу мүмкүн окшойт? Ооба, чындыгында, алар пайда болгон ... Дизайн үлгүсү "Стратегия" - 3
public class ChildrenBuggies extends Auto {

   public void fill() {

       //хм... Это детский багги, его не надо заправлять :/
   }
}
Биздин программада жалпы концепцияга туура келбеген унаа – балдар арабасы пайда болду. Ал педаль же радио менен башкарылышы мүмкүн, бирок бир нерсе анык - ага бензин куюуга эч кандай жер жок. Биздин тукум куучулук схемабыз жалпы методдорду аларга муктаж болбогон класстарга да берүүгө алып келди. Мындай кырдаалда эмне кылышыбыз керек? Мисалы, сиз fill()класстагы ыкманы жокко чыгарсаңыз болот ChildrenBuggies, ошондо сиз арабага май куюуга аракет кылганыңызда эч нерсе болбойт:
public class ChildrenBuggies extends Auto {

   @Override
   public void fill() {
       System.out.println("Игрушечную машину нельзя заправить!");
   }
}
Бирок бул чечимди ийгorктүү деп айтуу кыйын, жок дегенде codeдун кайталанышынан улам. Мисалы, көпчүлүк класстар ата-эне класстын ыкмасын колдонушат, бирок башка класстар аны жокко чыгарууга аргасыз болушат. Эгерде бизде 15 класс болсо, жана 5-6-да биз жүрүм-турумду жокко чыгарууга аргасыз болсок, codeдун кайталанышы кыйла кеңири болуп калат. Балким интерфейстер бизге жардам бере алат? Мисалы, бул:
public interface Fillable {

   public void fill();
}
FillableБиз бир ыкма менен интерфейс түзөбүз fill(). Демек, май куюу керек болгон унаалар бул интерфейсти ишке ашырат, ал эми башка унаалар (мисалы, биздин багги) ишке ашырbyte. Бирок бул вариант бизге да туура келбейт. Биздин класстык иерархия келечекте абдан чоң санга чейин өсүшү мүмкүн (дүйнөдө канча түрдүү унаа бар экенин элестетиңиз). Мурунку мурастоо опциясынан баш тарттык, анткени fill(). Бул жерде биз аны ар бир класста ишке ашырууга туура келет! Бизде алардын 50ү болсочу? Ал эми биздин программага тез-тез өзгөртүүлөр киргизилсе (жана реалдуу программаларда бул дээрлик дайыма боло берет!), биз 50 класстын ортосунда тorбиз менен чуркап, алардын ар биринин жүрүм-турумун кол менен өзгөртүүгө туура келет. Ошентип, биз акыры эмне кылышыбыз керек? Маселебизди чечүү үчүн башка жолду тандайлы. Тактап айтканда, биздин класстын жүрүм-турумун класстын өзүнөн бөлүп көрөлү. Бул эмнени билдирет? Белгилүү болгондой, ар кандай an objectтин абалы (маалыматтардын жыйындысы) жана жүрүм-туруму (методдордун жыйындысы) болот. Биздин машина классынын жүрүм-туруму үч ыкмадан турат - gas(), stop()жана fill(). Биринчи эки ыкма жакшы. Бирок биз үчүнчү ыкманы класстан тышкары жылдырабыз Auto. Бул жүрүм-турумду класстан бөлүү болот (тагыраак айтканда, жүрүм-турумдун бир бөлүгүн гана бөлөбүз – биринчи эки ыкма ордунда калат). Биздин ыкманы кайда жылдырышыбыз керек fill()? Эч нерсе дароо эле оюма келбейт :/ Ал толугу менен өз ордуна келгендей болду. Биз аны өзүнчө интерфейске жылдырабыз - FillStrategy!
public interface FillStrategy {

   public void fill();
}
Бул интерфейс бизге эмне үчүн керек? Баары оңой. Эми биз бул интерфейсти ишке ашыра турган бир нече класстарды түзө алабыз:
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("Просто заправляем бензин!");
   }
}
Биз үч жүрүм-турум стратегиясын түздүк - кадимки унаалар үчүн, гибриддер үчүн жана Формула 1 унаалары үчүн. Ар бир стратегия өзүнчө май куюу алгоритмин ишке ашырат. Биздин учурда, бул жөн гана консолго чыгарылат, бирок методдун ичинде кандайдыр бир татаал логика болушу мүмкүн. Муну менен эмне кылышыбыз керек?
public class Auto {

   FillStrategy fillStrategy;

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

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

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

}
Биз интерфейсибизди FillStrategyата-эне классындагы талаа катары колдонобуз Auto. Көңүл буруңуз: биз конкреттүү ишке ашырууну тактабайбыз, тескерисинче интерфейсти колдонобуз. FillStrategyБизге балдар унаасынын класстарында интерфейстин конкреттүү ишке ашырылышы керек болот :
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();
   }
}
Келгиле, бизде эмне бар экенин карап көрөлү:
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();
   }
}
Консолдук чыгаруу:

Просто заправляем бензин!
Заправляем бензином or электричеством на выбор!
Заправляем бензин только после всех остальных procedures пит-стопа!
Сонун, май куюу процесси каалагандай иштейт! Баса, стратегияны конструктордо параметр катары колдонууга эч нерсе тоскоол болбойт! Мисалы, бул сыяктуу:
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());
   }
}
Келгиле, биздин методубузду иштетели main()(ал өзгөрүүсүз бойдон калууда) жана ошол эле натыйжаны алабыз! Консолдук чыгаруу:

Просто заправляем бензин!
Заправляем бензином or электричеством на выбор!
Заправляем бензин только после всех остальных procedures пит-стопа!
Стратегиянын үлгүсү алгоритмдердин үй-бүлөсүн аныктайт, алардын ар бирин капсулдайт жана алардын бири-бирин алмаштыра алгандыгын камсыздайт. Ал алгоритмдерди кардар тарабынан колдонулушуна карабастан өзгөртүүгө мүмкүндүк берет (бул аныктама "Дизайн үлгүлөрүн изилдөө" китебинен алынган жана мага абдан ийгorктүү көрүнөт). Дизайн үлгүсү "Стратегия" - 4Бизди кызыктырган алгоритмдердин үй-бүлөсүн (май куюучу унаалардын түрлөрү) бир нече ишке ашыруу менен өзүнчө интерфейстерге бөлүп алдык. Биз аларды машинанын түпкү маңызынан ажыраттык. Ошондуктан, азыр биз тигил же бул май куюу процессине кандайдыр бир өзгөртүүлөрдү киргизүү керек болсо, бул биздин автоунаалардын класстарына эч кандай таасирин тийгизбейт. Алмаштыруучулукка келсек, ага жетүү үчүн классыбызга жөн гана бир сетер ыкмасын кошушубуз керек 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;
   }
}
Эми биз стратегияларды тез арада өзгөртө алабыз:
public class Main {

   public static void main(String[] args) {

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

       buggies.fill();
   }
}
Эгер күтүлбөгөн жерден балдардын унааларына бензин куюла баштаса, биздин программа ушундай сценарийге даяр болот :) Чындыгында ушунча! Сиз дагы бир дизайн үлгүсүн үйрөндүңүз, ал сизге сөзсүз керек болот жана реалдуу долбоорлордо иштеп жатканда сизге бир нече жолу жардам берет :) Дагы көрүшкөнчө!
Комментарийлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION