ตัวอย่างรูปแบบกลยุทธ์ในโลกแห่งความจริง


96

ฉันได้อ่านเกี่ยวกับหลักการของ OCPและวิธีใช้รูปแบบกลยุทธ์เพื่อบรรลุเป้าหมายนี้

ฉันจะพยายามอธิบายเรื่องนี้กับคนสองสามคน แต่ตัวอย่างเดียวที่ฉันคิดได้คือการใช้คลาสการตรวจสอบความถูกต้องที่แตกต่างกันตามสถานะของ "คำสั่งซื้อ"

ฉันได้อ่านบทความออนไลน์สองสามบทความ แต่สิ่งเหล่านี้มักไม่ได้อธิบายถึงเหตุผลที่เหมือนจริงในการใช้กลยุทธ์เช่นการสร้างรายงาน / ใบเรียกเก็บเงิน / การตรวจสอบความถูกต้อง ฯลฯ ...

มีตัวอย่างในโลกแห่งความเป็นจริงที่คุณคิดว่ารูปแบบกลยุทธ์เป็นเรื่องธรรมดาหรือไม่?

คำตอบ:


101

แล้วสิ่งนี้:

คุณต้องเข้ารหัสไฟล์

สำหรับไฟล์ขนาดเล็กคุณสามารถใช้กลยุทธ์ "ในหน่วยความจำ" ซึ่งไฟล์ที่สมบูรณ์จะถูกอ่านและเก็บไว้ในหน่วยความจำ (สมมติว่าสำหรับไฟล์ <1 gb)

สำหรับไฟล์ขนาดใหญ่คุณสามารถใช้กลยุทธ์อื่นโดยที่ส่วนต่างๆของไฟล์จะถูกอ่านในหน่วยความจำและผลลัพธ์ที่เข้ารหัสบางส่วนจะถูกเก็บไว้ในไฟล์ tmp

นี่อาจเป็นสองกลยุทธ์ที่แตกต่างกันสำหรับงานเดียวกัน

รหัสไคลเอ็นต์จะมีลักษณะเหมือนกัน:

 File file = getFile();
 Cipher c = CipherFactory.getCipher( file.size() );
 c.performAction();



// implementations:
interface  Cipher  {
     public void performAction();
}

class InMemoryCipherStrategy implements Cipher { 
         public void performAction() {
             // load in byte[] ....
         }
}

class SwaptToDiskCipher implements Cipher { 
         public void performAction() {
             // swapt partial results to file.
         }

}

     Cipher c = CipherFactory.getCipher( file.size() );

จะส่งคืนอินสแตนซ์กลยุทธ์ที่ถูกต้องสำหรับการเข้ารหัส

ฉันหวังว่านี่จะช่วยได้.

(ไม่รู้ด้วยซ้ำว่า Cipher เป็นคำที่ถูกหรือเปล่า: P)


8
ตัวอย่างของคุณไม่ใช่รูปแบบโรงงานหรือไม่? ฉันคิดว่ามันจะไม่ทำงานใน C # เช่น เมธอด "getCipher ()" ของคุณเป็นวิธีแบบคงที่ แต่ใน C # คุณไม่สามารถกำหนดวิธีการแบบคงที่บนอินเทอร์เฟซได้ (ฉันคิดว่าไม่ใช่ใน Java แต่สำหรับสิ่งนี้ฉันไม่แน่ใจ)
FrenchData

11
พวกเขาไปด้วยกัน โรงงานสร้างกลยุทธ์ แต่กลยุทธ์ของมันเองมีอัลกอริทึมในการดำเนินการ (โดยพื้นฐาน) เหมือนกัน กลยุทธ์ยังสามารถเปลี่ยนแปลงได้ในขณะรันไทม์ เกี่ยวกับวิธีการของโรงงานคุณถูกต้องฉันเปลี่ยนให้แล้ว
OscarRyz

หากต้องการเพิ่มจุด Osacars หากไม่มีโรงงานสามารถสร้างได้โดยไม่ต้องโรงงาน Cipher C =null; if (file.size() <= 2048) { C = new InMemoryCipherStrategy(); } else { c= SwaptToDiskCipher (); }
Abhijit Mazumder

เห็นด้วยกับ @FrenchData ในขณะที่การเป็นตัวอย่างที่ดีCipherFactoryอาจสร้างความสับสนให้กับผู้ที่ไม่คุ้นเคยกับรูปแบบกลยุทธ์
user487772

1
รูปแบบโรงงานเป็นเรื่องของการสร้างสรรค์กลยุทธ์เป็นเรื่องเกี่ยวกับพฤติกรรม มีสิทธิแตกต่างกันเล็กน้อย?
nhoxbypass

62

อีกครั้งเป็นโพสต์เก่า แต่ยังคงมีการค้นหาดังนั้นฉันจะเพิ่มอีกสองตัวอย่าง (รหัสอยู่ใน C #) ฉันชอบรูปแบบ Strategy มากเพราะมันช่วยก้นของฉันได้หลายครั้งเมื่อผู้จัดการโครงการพูดว่า: "เราต้องการให้แอปพลิเคชันทำ 'X' แต่ 'X' ยังไม่ชัดเจนและสามารถเปลี่ยนแปลงได้ในอนาคตอันใกล้นี้ " วิดีโอนี้อธิบายรูปแบบกลยุทธ์โดยใช้ StarCraft เป็นตัวอย่าง

สิ่งที่อยู่ในหมวดหมู่นี้:

  • การเรียงลำดับ: เราต้องการจัดเรียงตัวเลขเหล่านี้ แต่เราไม่รู้ว่าเราจะใช้ BrickSort, BubbleSort หรือการเรียงลำดับอื่น ๆ

  • การตรวจสอบความถูกต้อง: เราจำเป็นต้องตรวจสอบรายการตาม "กฎบางข้อ" แต่ยังไม่ชัดเจนว่ากฎนั้นจะเป็นอย่างไรและเราอาจคิดใหม่

  • เกม: เราต้องการให้ผู้เล่นเดินหรือวิ่งเมื่อเขาเคลื่อนไหว แต่ในอนาคตเขาควรจะสามารถว่ายน้ำบินเทเลพอร์ตขุดโพรงใต้ดิน ฯลฯ

  • การจัดเก็บข้อมูล: เราต้องการให้แอปพลิเคชันจัดเก็บข้อมูลลงในฐานข้อมูล แต่ในภายหลังอาจต้องสามารถบันทึกไฟล์หรือสร้างเว็บคอลได้

  • การส่งออก: เราจำเป็นต้องส่งออก X เป็นสตริงธรรมดา แต่ในภายหลังอาจเป็น CSV, XML, JSON และอื่น ๆ


ตัวอย่าง

ฉันมีโครงการที่ผู้ใช้สามารถกำหนดผลิตภัณฑ์ให้กับบุคคลในฐานข้อมูลได้ การมอบหมายผลิตภัณฑ์ให้กับบุคคลนี้มีสถานะเป็น "อนุมัติ" หรือ "ปฏิเสธ" ซึ่งขึ้นอยู่กับกฎทางธุรกิจบางประการ ตัวอย่างเช่นหากผู้ใช้กำหนดผลิตภัณฑ์ให้กับบุคคลที่มีอายุหนึ่งสถานะควรถูกปฏิเสธ หากความแตกต่างระหว่างสองช่องในรายการมีค่ามากกว่า 50 แสดงว่าสถานะถูกปฏิเสธเป็นต้น

ขณะนี้ในช่วงเวลาของการพัฒนากฎทางธุรกิจเหล่านี้ยังไม่ชัดเจนทั้งหมดและอาจมีกฎใหม่เกิดขึ้นได้ตลอดเวลา พลังของรูปแบบ stragety คือฉันสร้าง RuleAgent ซึ่งได้รับรายชื่อ IRules

public interface IRule {
    bool IsApproved(Assignment assignment); 
 }

ในขณะมอบหมายผลิตภัณฑ์ให้กับบุคคลหนึ่งฉันสร้าง RuleAgent ให้รายการกฎ (ซึ่งทั้งหมดใช้ IRule) และขอให้ตรวจสอบความถูกต้องของการมอบหมาย มันจะดำเนินไปตามกฎทั้งหมด ซึ่งเนื่องจากทั้งหมดใช้อินเทอร์เฟซเดียวกันทั้งหมดมีIsApprovedเมธอดและส่งคืนเท็จหากมีการคืนค่าเท็จ

เช่นเมื่อผู้จัดการปรากฏตัวขึ้นมาในทันใดและพูดว่าเราจำเป็นต้องปฏิเสธการมอบหมายงานทั้งหมดให้กับนักศึกษาฝึกงานหรือการมอบหมายงานทั้งหมดให้กับคนที่ทำงานล่วงเวลา ... คุณสร้างชั้นเรียนใหม่ดังนี้:

public OvertimeRule : IRule
{
    public bool IsApproved(Assignment assignment) //Interface method
    {
        if (assignment.Person.Timesheet >= 40)
        {
            return false;
        }
        return true;
    }
}

public InternRule : IRule
{
    public bool IsApproved(Assignment assignment) //Interface method
    {
        if (assignment.Person.Title == "Intern")
        {
            return false;
        }
        return true;
    }
}

คุณจะเห็นว่าคุณไม่จำเป็นต้องเพิ่มหรือลบ if-statement หรือ code ต่อไปเพียงแค่สร้างคลาสกฎใหม่ที่ใช้อินเทอร์เฟซ IRUle และเปลี่ยนสิ่งเหล่านั้นออกเมื่อจำเป็น


อีกตัวอย่างที่ยอดเยี่ยม: ซีรีส์วิดีโอของ Scott Allen ที่http://www.asp.net/mvc/pluralsightซึ่งเขาใช้รูปแบบกลยุทธ์ในส่วนการทดสอบหน่วยของแอปพลิเคชัน

เขาสร้างเว็บไซต์ที่มีหน้าที่แสดงรายการตามความนิยม อย่างไรก็ตาม "ยอดนิยม" อาจมีได้หลายอย่าง (จำนวนการดูส่วนใหญ่ผู้ติดตามส่วนใหญ่วันที่สร้างกิจกรรมส่วนใหญ่จำนวนความคิดเห็นน้อยที่สุด ฯลฯ ) และในกรณีที่การจัดการยังไม่ทราบวิธีการสั่งซื้อที่ชัดเจนและอาจต้องการทดลองกับสิ่งต่างๆ การสั่งซื้อในภายหลัง คุณสร้างอินเทอร์เฟซ (IOrderAlgorithm หรือบางสิ่งบางอย่าง) ด้วยวิธีการสั่งซื้อและปล่อยให้ Orderer-object มอบหมายการสั่งซื้อเพื่อนำอินเทอร์เฟซ IOrderAlgorithm ไปใช้อย่างเป็นรูปธรรม คุณสามารถสร้าง "CommentOrderer" "ActivityOrderer" ฯลฯ ... และเพียงแค่เปลี่ยนสิ่งเหล่านี้เมื่อมีข้อกำหนดใหม่


ฉันรู้ว่ามันอยู่นอกเหนือขอบเขตของคำถาม แต่จะเกิดอะไรขึ้นต่อไป? เรามีสิ่งนี้InternRuleแล้ว แต่เราจะเรียกใช้OvertimeRuleอย่างไร? เราจะแน่ใจได้อย่างไรว่าตรรกะใดก็ตามที่เรียกว่าOvertimeRule.IsApprovedตอนนี้เรียกด้วยInternRule.IsApproved?
Spencer Ruport

15

หมายเหตุสำคัญ:

  1. กลยุทธ์คือรูปแบบการออกแบบพฤติกรรม ใช้เพื่อสลับระหว่างกลุ่มอัลกอริทึม

  2. รูปแบบนี้ประกอบด้วยอินเทอร์เฟซกลยุทธ์นามธรรมหนึ่งรายการและการใช้กลยุทธ์ที่เป็นรูปธรรม ( อัลกอริทึม ) ของอินเทอร์เฟซนั้น

  3. แอปพลิเคชันใช้อินเทอร์เฟซกลยุทธ์เท่านั้น ทั้งนี้ขึ้นอยู่กับการกำหนดค่าพารามิเตอร์บางกลยุทธ์ที่เป็นรูปธรรมจะได้รับการติดแท็กอินเตอร์เฟซ

UML Diagram จากwikipedia

ป้อนคำอธิบายภาพที่นี่

ตัวอย่างเช่นคำหนึ่งจริง: สายการบินนำเสนอส่วนลดในบางช่วงเดือน (เดือนกรกฎาคมถึงธันวาคม) คุณสามารถมีโมดูลค่าโดยสารหนึ่งโมดูลซึ่งจะตัดสินใจเลือกตัวเลือกการกำหนดราคาโดยขึ้นอยู่กับจำนวนเดือน

ดูตัวอย่างง่ายๆ ตัวอย่างนี้สามารถขยายไปยังแอปพลิเคชันการค้าปลีกออนไลน์ซึ่งมอบส่วนลดให้กับสินค้าในตะกร้าสินค้าในวันพิเศษ / ชั่วโมงแห่งความสุขได้อย่างง่ายดาย

import java.util.*;

/* Interface for Strategy */
interface OfferStrategy {
    public String getName();
    public double getDiscountPercentage();
}
/* Concrete implementation of base Strategy */
class NoDiscountStrategy implements OfferStrategy{
    public String getName(){
        return this.getClass().getName();
    }
    public double getDiscountPercentage(){
        return 0;
    }
}
/* Concrete implementation of base Strategy */
class QuarterDiscountStrategy implements OfferStrategy{
    public String getName(){
        return this.getClass().getName();
    }
    public double getDiscountPercentage(){
        return 0.25;
    }
}
/* Context is optional. But if it is present, it acts as single point of contact
   for client. 

   Multiple uses of Context
   1. It can populate data to execute an operation of strategy
   2. It can take independent decision on Strategy creation. 
   3. In absence of Context, client should be aware of concrete strategies. Context acts a wrapper and hides internals
   4. Code re-factoring will become easy
*/
class StrategyContext {
    double price; // price for some item or air ticket etc.
    Map<String,OfferStrategy> strategyContext = new HashMap<String,OfferStrategy>();
    StrategyContext(double price){
        this.price= price;
        strategyContext.put(NoDiscountStrategy.class.getName(),new NoDiscountStrategy());
        strategyContext.put(QuarterDiscountStrategy.class.getName(),new QuarterDiscountStrategy());        
    }
    public void applyStrategy(OfferStrategy strategy){
        /* 
        Currently applyStrategy has simple implementation. You can use Context for populating some more information,
        which is required to call a particular operation            
        */
        System.out.println("Price before offer :"+price);
        double finalPrice = price - (price*strategy.getDiscountPercentage());
        System.out.println("Price after offer:"+finalPrice);
    }
    public OfferStrategy getStrategy(int monthNo){
        /*
            In absence of this Context method, client has to import relevant concrete Strategies everywhere.
            Context acts as single point of contact for the Client to get relevant Strategy
        */
        if ( monthNo < 6 )  {
            return strategyContext.get(NoDiscountStrategy.class.getName());
        }else{
            return strategyContext.get(QuarterDiscountStrategy.class.getName());
        }

    }
}
public class StrategyDemo{    
    public static void main(String args[]){
        StrategyContext context = new StrategyContext(100);
        System.out.println("Enter month number between 1 and 12");
        int month = Integer.parseInt(args[0]);
        System.out.println("Month ="+month);
        OfferStrategy strategy = context.getStrategy(month);
        context.applyStrategy(strategy);
    }

}

เอาท์พุท:

Enter month number between 1 and 12
Month =1
Price before offer :100.0
Price after offer:100.0

Enter month number between 1 and 12
Month =7
Price before offer :100.0
Price after offer:75.0

บทความที่เป็นประโยชน์:

รูปแบบกลยุทธ์โดย dzone

รูปแบบกลยุทธ์โดยการสร้างแหล่งที่มา


ขอบคุณ .... เข้าท่า .... เห็นด้วยใช้กลยุทธ์ () เป็นสัตว์ประหลาดที่บรรลุมนต์ดำมากมาย นอกจากนี้วิธีที่ไม่คงที่ในการถือกลยุทธ์ใน StrategyContext นั้นยอดเยี่ยมที่สุด
Arnab Dutta

12

ฉันนึกถึงตัวอย่างง่ายๆหลายอย่าง:

  • การจัดเรียงรายการ กลยุทธ์คือการเปรียบเทียบที่ใช้ในการตัดสินใจว่ารายการใดในสองรายการคือ "รายการแรก"
  • คุณอาจมีแอปพลิเคชันที่สามารถเลือกอัลกอริทึมการเรียงลำดับเอง (QuickSort, HeapSort ฯลฯ ) ในรันไทม์
  • ภาคผนวกเค้าโครงและตัวกรองในLog4NetและLog4j
  • ผู้จัดการเค้าโครงในชุดเครื่องมือ UI
  • การบีบอัดข้อมูล คุณอาจมีอินเทอร์เฟซ ICompressor ซึ่งวิธีการเดียวมีลักษณะดังนี้:

    ไบต์ [] บีบอัด (ไบต์ [] อินพุต);

    คลาสการบีบอัดคอนกรีตของคุณอาจเป็นเช่น RunLengthCompression, DeflateCompression เป็นต้น


9

การใช้รูปแบบกลยุทธ์ทั่วไปอย่างหนึ่งคือการกำหนดกลยุทธ์การเรียงลำดับที่กำหนดเอง (ในภาษาที่ไม่มีฟังก์ชันลำดับที่สูงกว่า) เช่นการจัดเรียงรายการของสตริงตามความยาวใน Java โดยส่งผ่านคลาสภายในที่ไม่ระบุชื่อ (การใช้อินเทอร์เฟซกลยุทธ์):

List<String> names = Arrays.asList("Anne", "Joe", "Harry");
Collections.sort(names, new Comparator<String>() {
  public int compare(String o1, String o2) {
    return o1.length() - o2.length();
  }
});
Assert.assertEquals(Arrays.asList("Joe", "Anne", "Harry"), names);

ในทำนองเดียวกันกลยุทธ์สามารถใช้สำหรับการสืบค้นเนทีฟกับฐานข้อมูลออบเจ็กต์เช่นใน db4o:

List<Document> set = db.query(new Predicate<Document>() {
  public boolean match(Document candidate) {
    return candidate.getSource().contains(source);
  }
});

8

ฉันมีแอปพลิเคชันที่ซิงโครไนซ์ฐานผู้ใช้ในแต่ละวันกับไดเรกทอรีองค์กรของเรา ผู้ใช้มีสิทธิ์หรือไม่มีสิทธิ์ขึ้นอยู่กับสถานะของพวกเขาในมหาวิทยาลัย ในแต่ละวันโปรแกรมการจัดเตรียมจะดำเนินการและตรวจสอบให้แน่ใจว่าผู้ที่ควรจะมีสิทธิ์ได้รับการจัดสรรในแอปพลิเคชันและผู้ที่ไม่ได้รับการยกเลิกการจัดเตรียม (จริงๆแล้วเป็นไปตามอัลกอริธึมการย่อยสลายที่สง่างาม แต่อยู่ข้างประเด็น) ในวันเสาร์ฉันทำการอัปเดตอย่างละเอียดมากขึ้นซึ่งจะซิงโครไนซ์คุณสมบัติบางอย่างของผู้ใช้แต่ละคนรวมทั้งตรวจสอบให้แน่ใจว่าพวกเขามีคุณสมบัติที่เหมาะสม ในตอนท้ายของเดือนฉันจะดำเนินการเรียกเก็บเงินคืนตามการใช้งานของเดือนนั้น

ฉันใช้รูปแบบกลยุทธ์แบบผสมเพื่อทำการซิงโครไนซ์นี้ โดยทั่วไปโปรแกรมหลักจะเลือกกลยุทธ์หลักโดยขึ้นอยู่กับวันในสัปดาห์ (การซิงค์เปลี่ยนแปลงเท่านั้น / ซิงค์ทั้งหมด) และเวลาของภาคการศึกษาที่สัมพันธ์กับปฏิทินการศึกษา หากรอบการเรียกเก็บเงินสิ้นสุดลงระบบจะรวบรวมด้วยกลยุทธ์การเรียกเก็บเงินด้วย จากนั้นเรียกใช้กลยุทธ์ที่เลือกผ่านอินเทอร์เฟซมาตรฐาน

ฉันไม่รู้ว่ามันธรรมดาแค่ไหน แต่ฉันรู้สึกว่ามันเข้ากันได้ดีกับรูปแบบกลยุทธ์


นี่เป็นตัวอย่างที่ดีมาก นอกจากนี้ยังบอกคุณอย่างชัดเจนถึงความแตกต่างระหว่างรูปแบบคำสั่งและกลยุทธ์โดยสรุป - เจตนา "โปรแกรมหลักโดยทั่วไปจะเลือกกลยุทธ์หลักขึ้นอยู่กับวันในสัปดาห์"
Utsav T

7

ฉันรู้ว่านี่เป็นคำถามเก่า แต่ฉันคิดว่าฉันมีอีกตัวอย่างที่น่าสนใจที่เพิ่งนำมาใช้

นี่เป็นตัวอย่างที่ใช้ได้จริงของรูปแบบกลยุทธ์ที่ใช้ในระบบการจัดส่งเอกสาร

ฉันมีระบบจัดส่ง PDF ซึ่งได้รับไฟล์เก็บถาวรที่มีเอกสารจำนวนมากและข้อมูลเมตาบางส่วน จากข้อมูลเมตาจะตัดสินใจว่าจะใส่เอกสารไว้ที่ใด พูดขึ้นอยู่กับข้อมูลที่ผมสามารถเก็บเอกสารในA, BหรือCระบบจัดเก็บข้อมูลหรือผสมของทั้งสาม

ลูกค้าต่างใช้ระบบนี้และพวกเขามีข้อกำหนดในการจัดการการย้อนกลับ / ข้อผิดพลาดที่แตกต่างกันในกรณีที่เกิดข้อผิดพลาด: เราต้องการให้ระบบการจัดส่งหยุดเมื่อเกิดข้อผิดพลาดแรกทิ้งเอกสารทั้งหมดที่จัดส่งไปแล้วในที่จัดเก็บของตน แต่หยุดกระบวนการและไม่ส่งมอบสิ่งอื่นใด ; อีกคนหนึ่งต้องการให้มันย้อนกลับจากBกรณีที่เกิดข้อผิดพลาดเมื่อจัดเก็บCแต่ปล่อยให้สิ่งที่ส่งไปAแล้ว มันง่ายที่จะจินตนาการว่าหนึ่งในสามหรือสี่จะมีความต้องการที่แตกต่างกัน

เพื่อแก้ปัญหานี้ฉันได้สร้างคลาสการจัดส่งพื้นฐานที่มีลอจิกการจัดส่งรวมถึงวิธีการย้อนกลับสิ่งของจากการจัดเก็บทั้งหมด วิธีการเหล่านั้นไม่ได้ถูกเรียกโดยระบบจัดส่งโดยตรงในกรณีที่เกิดข้อผิดพลาด แต่ชั้นเรียนจะใช้ Dependency Injection เพื่อรับคลาส "Rollback / Error Handling Strategy" (ตามลูกค้าที่ใช้ระบบ) ซึ่งเรียกว่าในกรณีที่มีข้อผิดพลาดซึ่งจะเรียกวิธีการย้อนกลับหากเหมาะสมกับกลยุทธ์นั้น

คลาสการจัดส่งเองรายงานว่าเกิดอะไรขึ้นกับคลาสกลยุทธ์ (เอกสารใดถูกส่งไปยังสิ่งที่จัดเก็บและความล้มเหลวเกิดขึ้น) และเมื่อใดก็ตามที่เกิดข้อผิดพลาดจะถามกลยุทธ์ว่าจะดำเนินการต่อหรือไม่ หากกลยุทธ์ระบุว่า "หยุดมัน" คลาสจะเรียกเมธอด "cleanUp" ของกลยุทธ์ซึ่งใช้ข้อมูลที่รายงานก่อนหน้านี้เพื่อตัดสินใจว่าจะเรียกวิธีการย้อนกลับจากคลาสการจัดส่งแบบใดหรือเพียงแค่ไม่ทำอะไรเลย

rollbackStrategy.reportSuccessA(...);
rollbackStrategy.reportFailureB(...);

if (rollbackStrategy.mustAbort()) {
    rollbackStrategy.rollback(); // rollback whatever is needed based on reports
    return false;
}

ดังนั้นตอนนี้ฉันมีสองกลยุทธ์ที่แตกต่างกัน: หนึ่งคือQuitterStrategy(ซึ่งออกจากข้อผิดพลาดแรกและไม่ล้างอะไรเลย) และอีกวิธีหนึ่งคือMaximizeDeliveryToAStrategy(ซึ่งพยายามให้มากที่สุดที่จะไม่ยกเลิกกระบวนการและไม่เคยย้อนกลับสิ่งที่ส่งไปยังที่จัดเก็บAแต่ ย้อนกลับสิ่งต่างๆจากBกรณีที่การจัดส่งCล้มเหลว)

จากความเข้าใจของฉันนี่เป็นตัวอย่างหนึ่งของรูปแบบกลยุทธ์ หากคุณ (ใช่คุณกำลังอ่าน) คิดว่าฉันคิดผิดโปรดแสดงความคิดเห็นด้านล่างและแจ้งให้เราทราบ ฉันอยากรู้ว่าอะไรจะถือเป็นการใช้รูปแบบกลยุทธ์ "บริสุทธิ์" และด้านใดของการนำไปใช้ที่ละเมิดคำจำกัดความ ฉันคิดว่ามันดูตลกไปหน่อยเพราะอินเทอร์เฟซกลยุทธ์ค่อนข้างอ้วน ตัวอย่างทั้งหมดที่ฉันเห็นจนถึงตอนนี้ใช้เพียงวิธีเดียว แต่ฉันก็ยังคิดว่านี่เป็นการสรุปอัลกอริทึม (หากตรรกะทางธุรกิจชิ้นหนึ่งถือได้ว่าเป็นอัลกอริทึมซึ่งฉันคิดว่าเป็นเช่นนั้น)

เนื่องจากกลยุทธ์ได้รับการแจ้งเตือนเกี่ยวกับเหตุการณ์ในระหว่างการดำเนินการจัดส่งจึงถือได้ว่าเป็นผู้สังเกตการณ์แต่นั่นเป็นอีกเรื่องหนึ่ง

จากการทำวิจัยน้อยก็ดูเหมือนว่านี้เป็น "รูปแบบคอมโพสิต" (เช่น MVC รูปแบบที่ใช้รูปแบบการออกแบบหลายใต้ในทางเฉพาะ) ที่เรียกว่าที่ปรึกษา เป็นที่ปรึกษาว่าการจัดส่งควรดำเนินต่อไปหรือไม่ แต่ยังเป็นตัวจัดการข้อผิดพลาดที่ใช้งานอยู่เนื่องจากสามารถย้อนกลับสิ่งต่างๆได้เมื่อถูกขอ

อย่างไรก็ตามนี่เป็นตัวอย่างที่ค่อนข้างซับซ้อนซึ่งอาจทำให้คุณรู้สึกว่าการใช้รูปแบบกลยุทธ์นั้นเรียบง่าย / งี่เง่าเกินไป มันอาจซับซ้อนมากและใช้ได้มากกว่าเมื่อใช้ร่วมกับรูปแบบอื่น ๆ


6

ตัวอย่างที่ดีของรูปแบบกลยุทธ์คือในเกมที่เราสามารถมีตัวละครที่แตกต่างกันและตัวละครแต่ละตัวสามารถมีอาวุธหลายแบบในการโจมตี แต่ในแต่ละครั้งสามารถใช้อาวุธได้เพียงชิ้นเดียว ดังนั้นเราจึงมีตัวละครตามบริบทเช่น King, Commander, Knight, Soldier และอาวุธเป็นกลยุทธ์ที่การโจมตี () อาจเป็นวิธีการ / อัลกอริทึมซึ่งขึ้นอยู่กับอาวุธที่ใช้ ดังนั้นหากคลาสอาวุธที่เป็นรูปธรรมคือ Sword, Ax, Crossbow, BowAndArrow และอื่น ๆ พวกเขาทั้งหมดจะใช้วิธีการโจมตี () ฉันแน่ใจว่าไม่จำเป็นต้องมีคำอธิบายเพิ่มเติม


1
ฉันคิดว่าคำตอบที่ได้รับการยอมรับควรจะพูดถึงตัวอย่างนี้ :)
Jeancarlo Fontalvo

6

รูปแบบกลยุทธ์เป็นรูปแบบที่ใช้กันมากที่สุดโดยเฉพาะสำหรับการตรวจสอบความถูกต้องและอัลกอริทึมการเรียงลำดับ

ให้ฉันอธิบายด้วยตัวอย่างการปฏิบัติง่ายๆ

enum Speed {
  SLOW, MEDIUM, FAST;
}

class Sorter {
 public void sort(int[] input, Speed speed) {
    SortStrategy strategy = null;
    switch (speed) {
    case SLOW:
        strategy = new SlowBubbleSortStrategy();
        break;
    case MEDIUM:
        strategy = new MediumInsertationSortStrategy();
        break;

    case FAST:
        strategy = new FastQuickSortStrategy();
        break;
    default:
        strategy = new MediumInsertationSortStrategy();
    }
    strategy.sort(input);
 }

}

interface SortStrategy {

    public void sort(int[] input);
}

class SlowBubbleSortStrategy implements SortStrategy {

   public void sort(int[] input) {
    for (int i = 0; i < input.length; i++) {
        for (int j = i + 1; j < input.length; j++) {
            if (input[i] > input[j]) {
                int tmp = input[i];
                input[i] = input[j];
                input[j] = tmp;
            }
        }
    }
    System.out.println("Slow sorting is done and the result is :");
    for (int i : input) {
        System.out.print(i + ",");
    }
  }

 }

class MediumInsertationSortStrategy implements SortStrategy {

public void sort(int[] input) {
    for (int i = 0; i < input.length - 1; i++) {
        int k = i + 1;
        int nxtVal = input[k];
        while (input[k - 1] > nxtVal) {
            input[k] = input[k - 1];
            k--;
            if (k == 0)
                break;
        }
        input[k] = nxtVal;
    }
    System.out.println("Medium sorting is done and the result is :");
    for (int i : input) {
        System.out.print(i + ",");
    }

 }

}

class FastQuickSortStrategy implements SortStrategy {

public void sort(int[] input) {
    sort(input, 0, input.length-1);
    System.out.println("Fast sorting is done and the result is :");
    for (int i : input) {
        System.out.print(i + ",");
    }
}

private void sort(int[] input, int startIndx, int endIndx) {
    int endIndexOrig = endIndx;
    int startIndexOrig = startIndx;
    if( startIndx >= endIndx)
        return;
    int pavitVal = input[endIndx];
    while (startIndx <= endIndx) {
        while (input[startIndx] < pavitVal)
            startIndx++;
        while (input[endIndx] > pavitVal)
            endIndx--;
        if( startIndx <= endIndx){
            int tmp = input[startIndx];
            input[startIndx] = input[endIndx];
            input[endIndx] = tmp;
            startIndx++;
            endIndx--;
        }
    }
    sort(input, startIndexOrig, endIndx);
    sort(input, startIndx, endIndexOrig);
 }

}  

รหัสทดสอบสำหรับสิ่งนี้คือ

public class StrategyPattern {
  public static void main(String[] args) {
    Sorter sorter = new Sorter();
    int[] input = new int[] {7,1,23,22,22,11,0,21,1,2,334,45,6,11,2};
    System.out.print("Input is : ");
    for (int i : input) {
        System.out.print(i + ",");
    }
    System.out.println();
    sorter.sort(input, Speed.SLOW);
 }

}

ตัวอย่างเดียวกันนี้นำมาจากhttp://coder2design.com/strategy-pattern/


การใช้รูปแบบกลยุทธ์ที่แตกต่างกัน: การตรวจสอบความถูกต้อง: เมื่อมีการตรวจสอบความถูกต้องมากมายในโค้ดของคุณ อัลกอริทึมที่แตกต่างกัน: โดยเฉพาะอย่างยิ่งเมื่อสามารถใช้อัลกอริทึมการเรียงลำดับที่แตกต่างกันเช่นการเรียงฟองหรือการเรียงลำดับอย่างรวดเร็ว การจัดเก็บข้อมูล: เมื่อเราอาจให้ข้อมูลในสถานที่ที่แตกต่างกันเช่นฐานข้อมูลหรือระบบไฟล์ การแยกวิเคราะห์: ในขณะที่แยกวิเคราะห์เราสามารถใช้กลยุทธ์ที่แตกต่างกันสำหรับอินพุตที่แตกต่างกัน กลยุทธ์การกรอง กลยุทธ์การจัดวาง
Jatinder Pal

2

ฉันใช้แนวทางกลยุทธ์ในกลไกที่ค่อนข้างซับซ้อนในแอปพลิเคชันที่เป็นตัวอย่างที่ดี โดยพื้นฐานแล้วบทบาทของเครื่องยนต์คือการไปและก่อนอื่นให้ค้นหารายชื่อผู้ที่มีวิดเจ็ตบทบาทที่สองคือการค้นหาว่าใครคือคนที่ดีที่สุด 10 คนที่มีวิดเจ็ตตามจำนวนพารามิเตอร์ที่ไม่รู้จัก (เช่นระยะห่างของราคาธุรกิจก่อนหน้านี้ด้วยกัน จำนวนสินค้าในสต็อกตัวเลือกการจัดส่ง ฯลฯ ฯลฯ ฯลฯ ... )

โดยพื้นฐานแล้วสิ่งที่เราทำคือเราแบ่งปัญหาออกเป็นสองกลยุทธ์แรกคือการดึงข้อมูลเนื่องจากเรารู้ว่าเรามีแหล่งที่มาของวิดเจ็ตหลายแหล่งและเราจำเป็นต้องสามารถรับข้อมูลและแปลงเป็นโครงสร้างทั่วไปได้

จากนั้นเราก็ตระหนักว่าเรามีหลายอัลกอริทึมบางอย่างขึ้นอยู่กับการถ่วงน้ำหนักพารามิเตอร์อื่น ๆ ก็แปลกและเป็นลางบอกเหตุและฉันไม่สามารถทำมันได้อย่างยุติธรรมโดยไม่ดึงภาพและแผนภูมิออกมาและคุณก็จะได้ภาพเรามีอัลกอริทึมมากมายสำหรับ คัดเลือกคนที่ดีที่สุด

บริการของเราเป็นสิ่งที่กำหนดอินพุตเอาต์พุตและการทำให้ข้อมูลเป็นมาตรฐานเป็นหลักนอกจากนี้ยังใช้รูปแบบผู้ให้บริการเพื่อปลั๊กอินผู้ให้บริการข้อมูลเฉพาะแอปพลิเคชันและผู้ให้บริการอัลกอริทึมที่ใช้กลยุทธ์ นี่เป็นระบบที่มีประสิทธิภาพพอสมควร

เรามีการถกเถียงกันบ้างหากเราใช้กลยุทธ์หรือรูปแบบเทมเพลตที่เราไม่เคยแก้ไข


2

คุณแน่ใจหรือไม่ว่าสถานะของ "คำสั่งซื้อ" ไม่ใช่รูปแบบของรัฐ ฉันมีลางสังหรณ์ว่าคำสั่งซื้อจะไม่ได้รับการจัดการที่แตกต่างกันขึ้นอยู่กับสถานะ

ยกตัวอย่างเช่นวิธีการจัดส่งตามคำสั่งซื้อ:

order.Ship();
  • หากวิธีการจัดส่งแตกต่างกันไปตามหน้าที่ของสถานะแสดงว่าคุณมีรูปแบบกลยุทธ์
  • อย่างไรก็ตามหากวิธีการShip ()ประสบความสำเร็จก็ต่อเมื่อชำระเงินตามคำสั่งซื้อแล้วและยังไม่มีการจัดส่งคำสั่งซื้อแสดงว่าคุณมีรูปแบบสถานะ

ตัวอย่างที่ดีที่สุดของรูปแบบรัฐ (และรูปแบบอื่น ๆ ) ที่ฉันพบอยู่ในหนังสือ " Head First Design Patterns " ซึ่งน่าทึ่งมาก เป็นครั้งที่สองใกล้จะเป็นเดวิด Cumps' บล็อกชุดของรูปแบบ


2

สมมติว่าคุณต้องการเขียนอัลกอริทึมเพื่อคำนวณXday ที่ nของเดือนและปีที่กำหนดเช่นวันจันทร์ที่สองของเดือนตุลาคม 2014 คุณต้องการใช้คลาส Time ของ Android android.text.format.Timeเพื่อแสดงวันที่ แต่คุณต้องการเขียนอัลกอริทึมทั่วไปด้วย java.util.Calendarที่ยังสามารถนำไปใช้กับ

นี่คือสิ่งที่ฉันทำ

ใน DatetimeMath.java:

public interface DatetimeMath { 
    public Object createDatetime(int year, int month, int day);

    public int getDayOfWeek(Object datetime);

    public void increment(Object datetime);
}

ใน TimeMath.java:

public class TimeMath implements DatetimeMath {
    @Override
    public Object createDatetime(int year, int month, int day) {
        Time t = new Time();
        t.set(day, month, year);
        t.normalize(false);
        return t;
    }

    @Override
    public int getDayOfWeek(Object o) {
        Time t = (Time)o;
        return t.weekDay;
    }   

    @Override
    public void increment(Object o) {
        Time t = (Time)o;
        t.set(t.monthDay + 1, t.month, t.year);
        t.normalize(false);
    }
}

ใน OrdinalDayOfWeekCalculator.java คลาสที่มีอัลกอริทึมทั่วไป:

public class OrdinalDayOfWeekCalculator {   
    private DatetimeMath datetimeMath;

    public OrdinalDayOfWeekCalculator(DatetimeMath m) {
        datetimeMath = m;
    }

    public Object getDate(int year, int month, int dayOfWeek, int ordinal) {
        Object datetime = datetimeMath.createDatetime(year, month, 1);
        if (datetimeMath.getDayOfWeek(datetime) == dayOfWeek) {
            return datetime;
        } 
        int xDayCount = 0;
        while (xDayCount != ordinal) {
            datetimeMath.increment(datetime);
            if (datetimeMath.getDayOfWeek(datetime) == dayOfWeek) {
                xDayCount++;
            }
        }
        return datetime;
    }
}

ในแอป Android ของฉันฉันจะเรียกสิ่งที่ต้องการ

OrdinalDayOfWeekCalculator odowc = 
        new OrdinalDayOfWeekCalculator(new TimeMath());
Time canadianThanksgiving = (Time)odowc.getDate(
        year, Calendar.OCTOBER, Time.MONDAY, 2);

ถ้าฉันต้องการใช้อัลกอริทึมเดียวกันซ้ำjava.util.Calendarฉันจะเขียนคลาส CalendarMath ที่ใช้สามวิธีใน DatetimeMath จากนั้นใช้

OrdinalDayOfWeekCalculator odowc2 = 
        new OrdinalDayOfWeekCalculator(new CalendarMath());
Calendar canadianThanksgivingCal = (Calendar)odowc2.getDate(
        year, Calendar.OCTOBER, Calendar.MONDAY, 2);

2
public class StrategyDemo {
    public static void main(String[] args) {
        ShoppingCart cart = new ShoppingCart();

        Item item1 = new Item("1234", 10);
        Item item2 = new Item("5678", 40);

        cart.addItem(item1);
        cart.addItem(item2);

        // pay by paypal
        cart.pay(new PaypalStrategy("myemail@example.com", "mypwd"));

        // pay by credit card
        cart.pay(new CreditCardStrategy("Pankaj Kumar", "1234567890123456", "786", "12/15"));
    }
}

interface PaymentStrategy {
    public void pay(int amount);
}

class CreditCardStrategy implements PaymentStrategy {

    private String name;
    private String cardNumber;
    private String cvv;
    private String dateOfExpiry;

    public CreditCardStrategy(String nm, String ccNum, String cvv, String expiryDate) {
        this.name = nm;
        this.cardNumber = ccNum;
        this.cvv = cvv;
        this.dateOfExpiry = expiryDate;
    }

    @Override
    public void pay(int amount) {
        System.out.println(amount + " paid with credit/debit card");
    }

}

class PaypalStrategy implements PaymentStrategy {

    private String emailId;
    private String password;

    public PaypalStrategy(String email, String pwd) {
        this.emailId = email;
        this.password = pwd;
    }

    @Override
    public void pay(int amount) {
        System.out.println(amount + " paid using Paypal.");
    }

}

class Item {

    private String upcCode;
    private int price;

    public Item(String upc, int cost) {
        this.upcCode = upc;
        this.price = cost;
    }

    public String getUpcCode() {
        return upcCode;
    }

    public int getPrice() {
        return price;
    }

}

class ShoppingCart {

    // List of items
    List<Item> items;

    public ShoppingCart() {
        this.items = new ArrayList<Item>();
    }

    public void addItem(Item item) {
        this.items.add(item);
    }

    public void removeItem(Item item) {
        this.items.remove(item);
    }

    public int calculateTotal() {
        int sum = 0;
        for (Item item : items) {
            sum += item.getPrice();
        }
        return sum;
    }

    public void pay(PaymentStrategy paymentMethod) {
        int amount = calculateTotal();
        paymentMethod.pay(amount);
    }
}

1

ไม่กี่สัปดาห์ที่ผ่านมาฉันได้เพิ่มอินเทอร์เฟซ Java ทั่วไปซึ่งใช้งานโดยอ็อบเจ็กต์โดเมนของเรา อ็อบเจ็กต์โดเมนนี้ถูกโหลดจากฐานข้อมูลและการแสดงฐานข้อมูลเป็นสคีมาแบบดาวที่มีสาขาประมาณ 10+ สาขา ผลที่ตามมาอย่างหนึ่งของการมีอ็อบเจ็กต์โดเมนที่มีน้ำหนักมากคือเราต้องสร้างอ็อบเจ็กต์โดเมนอื่น ๆ ที่แสดงถึงสคีมาเดียวกันแม้ว่าจะมีน้ำหนักน้อยกว่าก็ตาม ดังนั้นฉันจึงทำให้วัตถุน้ำหนักเบาอื่น ๆ ใช้อินเทอร์เฟซเดียวกัน เรามี:

public interface CollectibleElephant { 
    long getId();
    String getName();
    long getTagId();
}

public class Elephant implements CollectibleElephant { ... }
public class BabyElephant implements CollectibleElephant { ... }

แต่เดิมฉันต้องการใช้CollectibleElephantเพื่อเรียงลำดับElephants ค่อนข้างเร็วเพื่อนร่วมทีมของฉันพยายามที่CollectibleElephantจะเรียกใช้การตรวจสอบความปลอดภัยกรองพวกเขาเมื่อพวกเขาถูกส่งไปยัง GUI ฯลฯ


1

เราต้องสร้างอินเทอร์เฟซการจัดเตรียมของบุคคลที่สามสำหรับแพลตฟอร์มระดับองค์กรที่มีฐานข้อมูลที่ซับซ้อนมาก การส่งข้อมูลที่จะจัดเตรียมเป็นรายการประเภทข้อมูลของเราซึ่งรวมอยู่ในคิวลำดับความสำคัญในแอปพลิเคชันของเราเพื่อให้สามารถเขียนไปยังฐานข้อมูลตามลำดับที่ถูกต้องเนื่องจากการอ้างอิง

ขั้นตอนการเขียนข้อมูลนั้นค่อนข้างง่ายให้โผล่ออกมาจากลำดับความสำคัญบนสุดของคิวจากนั้นเลือกกลยุทธ์ตามประเภทของวัตถุที่คุณแยกออกมา


0

จาก wikipedia

ในการเขียนโปรแกรมคอมพิวเตอร์รูปแบบกลยุทธ์ (หรือที่เรียกว่ารูปแบบนโยบาย) เป็นรูปแบบการออกแบบซอฟต์แวร์เชิงพฤติกรรมที่ช่วยให้สามารถเลือกอัลกอริทึมขณะรันไทม์ได้ แทนที่จะใช้อัลกอริทึมเดียวโดยตรงโค้ดจะได้รับคำแนะนำในการรันไทม์ซึ่งในตระกูลของอัลกอริทึมที่จะใช้

ในแอปพลิเคชัน Windows Paint คุณจะเห็นรูปแบบกลยุทธ์ที่คุณสามารถเลือกรูปร่างและสีได้อย่างอิสระในส่วนต่างๆ รูปร่างและสีคืออัลกอริทึมที่สามารถเปลี่ยนแปลงได้ในขณะรันไทม์

หากคุณต้องการวาดวงกลมด้วยสีแดงแทนที่จะให้ตัวเลือก 'RedCircle' จะให้คุณเลือกวงกลมและสีที่คุณต้องการ

Shape redCircle = new RedCircle(); // Without stretegy Pattern
Shaped redCircle = new Shape("red","circle"); // With Strategy pattern

หากไม่มีรูปแบบกลยุทธ์จะเพิ่มจำนวนคลาสด้วยผลิตภัณฑ์คาร์ทีเซียนที่มีรูปร่างและสี นอกจากนี้อินเทอร์เฟซยังเปลี่ยนแปลงสำหรับการใช้งานแต่ละครั้ง


0

ลองนึกภาพเกมยิงที่มีศัตรู AI คุณต้องการให้พวกเขาต่อสู้ในรูปแบบต่างๆอย่างต่อเนื่องตามสิ่งที่เกิดขึ้น .. ด้วยรูปแบบกลยุทธ์คุณสามารถวนซ้ำได้อย่างต่อเนื่องและเปลี่ยนแปลงวิธีการดำเนินการเฉพาะหรือการกระทำแบบไดนามิก

interface FightingStategy{
    public void fight();
}
public Defense implements FightingStrategy{
    public void figth(){
        ... hide behind wall to shoot
    }
}
public Berserker implements FightingStrategy{
    public void fight(){
        ... run towards you, headrolls and shoots
    }
}
public Dead implements FightingStrategy{
    public void fight(){
        ... is dead, doesn't move
    }
}

public AiShooter{

    FightingStrategy fightingStrategy;

    public AiShooter(){
        fightStrategy = new Berserker();
    }

    public void fight(){
        this.fightingStrategy.fight();
    }

    public void changeStrategy(FightingStrategy f){
        this.fightingStrategy = f;
    }
}

public static void main(){

    ... create list of AiShooters...
    while (condition){
        list.forEach(shooter -> shooter.fight());
    }
    ... you shoot back
    list.ForEach(shooter -> shooter.changeStrategy(new 
Defense()));

    ... you kill one
    list.get(n).changeStrategy(new Dead());
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.