Ví dụ về mô hình chiến lược trong thế giới thực


94

Tôi đã đọc về nguyên tắc OCP và cách sử dụng mô hình chiến lược để thực hiện điều này.

Tôi đã cố gắng giải thích điều này cho một vài người, nhưng ví dụ duy nhất tôi có thể nghĩ đến là sử dụng các lớp xác nhận khác nhau dựa trên trạng thái của một "đơn hàng".

Tôi đã đọc một vài bài báo trực tuyến, nhưng những bài báo này thường không mô tả lý do thực sự giống như lý do để sử dụng chiến lược, như tạo báo cáo / hóa đơn / xác thực, v.v.

Có ví dụ thực tế nào mà bạn cho rằng một mô hình chiến lược là phổ biến không?

Câu trả lời:


99

Cái này thì sao:

Bạn phải mã hóa một tệp.

Đối với các tệp nhỏ, bạn có thể sử dụng chiến lược "trong bộ nhớ", nơi tệp hoàn chỉnh được đọc và lưu trong bộ nhớ (giả sử đối với tệp <1 gb)

Đối với các tệp lớn, bạn có thể sử dụng một chiến lược khác, trong đó các phần của tệp được đọc trong bộ nhớ và kết quả được mã hóa một phần được lưu trữ trong tệp tmp.

Đây có thể là hai chiến lược khác nhau cho cùng một nhiệm vụ.

Mã khách hàng sẽ giống nhau:

 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.
         }

}

Các

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

Sẽ trả về trường hợp chiến lược chính xác cho mật mã.

Tôi hi vọng cái này giúp được.

(Tôi thậm chí không biết Cipher có phải là từ thích hợp không: P)


8
Ví dụ của bạn không phải là một Mô hình Nhà máy? Ngoài ra, tôi nghĩ rằng nó sẽ không hoạt động trong C # chẳng hạn. Phương thức "getCipher ()" của bạn là một phương thức tĩnh nhưng trong C #, bạn không thể xác định một phương thức tĩnh trên một giao diện (tôi không nghĩ vậy trong Java nhưng tôi không chắc về điều này).
Tiếng PhápData

10
Họ đi cùng nhau. Nhà máy tạo ra chiến lược, nhưng chiến lược mà nó tự nắm giữ thuật toán để thực hiện (về cơ bản) hoạt động giống nhau. Chiến lược cũng có thể được thay đổi trong thời gian chạy. Về phương pháp nhà máy, bạn đúng, tôi đã thay đổi nó.
OscarRyz

Để thêm vào Osacars điểm, mà không cần nhà máy này có thể được tạo ra mà không nhà máy Cipher C =null; if (file.size() <= 2048) { C = new InMemoryCipherStrategy(); } else { c= SwaptToDiskCipher (); }
Abhijit Mazumder

Đồng ý với @FrenchData. Mặc dù là một ví dụ tuyệt vời, sự hiện diện của CipherFactorycó thể khiến những người không quen thuộc với mô hình Chiến lược nhầm lẫn.
user487772

1
Mô hình nhà máy là về sáng tạo, Chiến lược là về Hành vi. Có một chút khác nhau phải không?
nhoxbypass

61

Một lần nữa, một bài đăng cũ nhưng vẫn xuất hiện trên các tìm kiếm nên tôi sẽ thêm hai ví dụ nữa (Mã bằng C #). Tôi hoàn toàn thích mô hình Chiến lược vì nó đã giúp tôi rất nhiều lần khi các nhà quản lý dự án nói: "Chúng tôi muốn ứng dụng làm 'X', nhưng 'X' vẫn chưa rõ ràng và nó có thể thay đổi trong tương lai gần." " Video này giải thích mô hình chiến lược , sử dụng StarCraft làm ví dụ.

Nội dung thuộc danh mục này:

  • Sắp xếp: Chúng tôi muốn sắp xếp những số này, nhưng chúng tôi không biết liệu chúng tôi sẽ sử dụng BrickSort, BubbleSort hay một số cách sắp xếp khác

  • Xác thực: Chúng tôi cần kiểm tra các mục theo "Quy tắc nào đó", nhưng vẫn chưa rõ quy tắc đó sẽ là gì và chúng tôi có thể nghĩ ra những quy tắc mới.

  • Trò chơi: Chúng tôi muốn người chơi có thể đi bộ hoặc chạy khi anh ta di chuyển, nhưng có thể trong tương lai, anh ta cũng có thể bơi, bay, dịch chuyển tức thời, đào hang dưới lòng đất, v.v.

  • Lưu trữ thông tin: Chúng tôi muốn ứng dụng lưu trữ thông tin vào Cơ sở dữ liệu, nhưng sau này ứng dụng có thể phải lưu tệp hoặc thực hiện cuộc gọi trên web

  • Kết xuất: Chúng ta cần xuất X dưới dạng chuỗi thuần túy, nhưng sau đó có thể là CSV, XML, JSON, v.v.


Ví dụ

Tôi có một dự án mà người dùng có thể gán sản phẩm cho mọi người trong cơ sở dữ liệu. Việc giao sản phẩm cho một người có trạng thái là "Được chấp thuận" hoặc "Bị từ chối", điều này phụ thuộc vào một số quy tắc kinh doanh. Ví dụ: nếu người dùng gán sản phẩm cho một người ở độ tuổi nhất định, thì trạng thái của sản phẩm đó sẽ bị từ chối; Nếu sự khác biệt giữa hai trường trong mục lớn hơn 50, trạng thái của mục đó bị từ chối, v.v.

Hiện tại, tại thời điểm phát triển, các quy tắc kinh doanh này vẫn chưa hoàn toàn rõ ràng và các quy tắc mới có thể ra đời bất cứ lúc nào. Sức mạnh của mô hình đi lạc là tôi đã tạo một RuleAgent, nó được cung cấp một danh sách các IRules.

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

Tại thời điểm chỉ định một sản phẩm cho một người, tôi tạo một RuleAgent, cung cấp cho nó một danh sách các quy tắc (tất cả các quy tắc này đều triển khai IRule) và yêu cầu nó xác thực một nhiệm vụ. Nó sẽ chạy qua tất cả các quy tắc của nó. Bởi vì tất cả chúng đều triển khai cùng một giao diện, tất cả đều có IsApprovedphương thức và trả về false nếu bất kỳ phương thức nào trong số chúng trả về false.

Ví dụ, khi người quản lý đột nhiên đến và nói, chúng ta cũng cần từ chối tất cả các nhiệm vụ cho sinh viên thực tập, hoặc tất cả các nhiệm vụ cho những người làm việc ngoài giờ ... Bạn tạo các lớp học mới như sau:

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;
    }
}

Bạn thấy rằng bạn không phải tiếp tục thêm hoặc xóa các câu lệnh if hoặc mã, chỉ cần tạo một lớp quy tắc mới triển khai giao diện IRUle và chuyển chúng ra khi cần.


Một ví dụ tuyệt vời khác: loạt video của Scott Allen tại http://www.asp.net/mvc/pluralsight , nơi anh ấy sử dụng mẫu chiến lược trong phần Unit-test của ứng dụng

Anh ấy xây dựng một trang web có trang hiển thị các mặt hàng dựa trên mức độ phổ biến. Tuy nhiên, "Phổ biến" có thể là nhiều thứ (hầu hết lượt xem, hầu hết người đăng ký, ngày tạo, hoạt động nhiều nhất, lượng nhận xét ít nhất, v.v.) và trong trường hợp ban quản lý chưa biết chính xác cách đặt hàng và có thể muốn thử nghiệm các thử thách vào một ngày sau đó. Bạn tạo một giao diện (IOrderAlgorithm hoặc một cái gì đó) với một phương thức đặt hàng và để một đối tượng Order ủy quyền thứ tự cho một triển khai cụ thể của giao diện IOrderAlgorithm. Bạn có thể tạo một "CommentOrderer", "ActivityOrderer", v.v ... Và chỉ cần chuyển chúng ra khi có yêu cầu mới.


Tôi biết điều này hơi nằm ngoài phạm vi của câu hỏi nhưng điều gì xảy ra tiếp theo? Chúng tôi có điều này InternRulengay bây giờ nhưng chúng tôi kích hoạt OvertimeRulenhư thế nào? Làm thế nào để chúng ta đảm bảo rằng bất kỳ logic nào được gọi OvertimeRule.IsApprovedbây giờ cũng gọi InternRule.IsApproved?
Spencer Ruport

13

Ghi chú chính:

  1. Chiến lược là mẫu thiết kế hành vi. Nó được sử dụng để chuyển đổi giữa các họ thuật toán.

  2. Mẫu này chứa một giao diện chiến lược trừu tượng và nhiều triển khai chiến lược cụ thể ( thuật toán ) của giao diện đó.

  3. Ứng dụng chỉ sử dụng giao diện chiến lược . Tùy thuộc vào một số thông số cấu hình, chiến lược cụ thể sẽ được gắn thẻ vào giao diện .

Sơ đồ UML từ wikipedia

nhập mô tả hình ảnh ở đây

Một ví dụ từ thực tế: Các hãng hàng không giảm giá trong một số tháng (tháng 7 đến tháng 12) . Bạn có thể có một mô-đun Giá vé , mô-đun này quyết định các tùy chọn giá cả tùy thuộc vào số tháng.

Hãy xem một ví dụ đơn giản. Ví dụ này có thể được mở rộng cho các ứng dụng bán lẻ trực tuyến, cung cấp giảm giá cho các mặt hàng trong giỏ hàng vào những ngày đặc biệt / giờ khuyến mãi một cách dễ dàng.

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);
    }

}

đầu ra:

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

Bài viết hữu ích:

mô hình chiến lược của dzone

mô hình chiến lược bằng cách tạo nguồn


12

Tôi có thể nghĩ về một số ví dụ khá đơn giản:

  • Sắp xếp danh sách. Chiến lược là so sánh được sử dụng để quyết định mục nào trong hai mục trong danh sách là "Đầu tiên"
  • Bạn có thể có một ứng dụng trong đó chính thuật toán sắp xếp (QuickSort, HeapSort, v.v.) có thể được chọn trong thời gian chạy
  • Trình bổ sung, Bố cục và Bộ lọc trong Log4NetLog4j
  • Trình quản lý bố cục trong bộ công cụ giao diện người dùng
  • Nén dữ liệu. Bạn có thể có một giao diện ICompressor có phương thức duy nhất trông giống như sau:

    byte [] nén (byte [] đầu vào);

    Các lớp nén cụ thể của bạn có thể là những thứ như RunLengthCompression, DeflateCompression, v.v.


9

Một cách sử dụng phổ biến của mẫu chiến lược là xác định các chiến lược sắp xếp tùy chỉnh (trong các ngôn ngữ không có hàm bậc cao hơn), ví dụ: để sắp xếp danh sách các chuỗi theo độ dài trong Java, chuyển một lớp ẩn danh bên trong (một triển khai của giao diện chiến lược):

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);

Theo cách tương tự, các chiến lược có thể được sử dụng cho các truy vấn gốc với cơ sở dữ liệu đối tượng, ví dụ: trong db4o:

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

8

Tôi có một ứng dụng đồng bộ hóa cơ sở người dùng của nó mỗi ngày với thư mục doanh nghiệp của chúng tôi. Người dùng đủ điều kiện hoặc không đủ điều kiện dựa trên trạng thái của họ trong trường Đại học. Mỗi ngày chương trình cung cấp đều hoạt động và đảm bảo rằng những người được cho là đủ điều kiện sẽ được cấp phép trong ứng dụng và những người không được hủy cấp phép (thực tế là theo một thuật toán giảm cấp duyên dáng, nhưng đó là điều bên cạnh vấn đề). Vào thứ Bảy, tôi thực hiện một bản cập nhật kỹ lưỡng hơn để đồng bộ hóa một số thuộc tính của từng người dùng cũng như đảm bảo rằng họ có đủ điều kiện thích hợp. Vào cuối tháng, tôi thực hiện một số xử lý hóa đơn dựa trên mức sử dụng của tháng đó.

Tôi sử dụng một mẫu chiến lược có thể kết hợp để thực hiện việc đồng bộ hóa này. Chương trình chính về cơ bản chọn một chiến lược tổng thể tùy thuộc vào ngày trong tuần (đồng bộ hóa chỉ thay đổi / đồng bộ hóa tất cả) và thời gian của học kỳ liên quan đến lịch học. Nếu chu kỳ thanh toán đang kết thúc, thì chu kỳ thanh toán cũng sẽ kết hợp nó với một chiến lược thanh toán. Sau đó, nó chạy chiến lược đã chọn thông qua một giao diện tiêu chuẩn.

Tôi không biết điều này phổ biến như thế nào, nhưng tôi cảm thấy nó hoàn toàn phù hợp với mô hình chiến lược.


Đây là một ví dụ rất tốt. Ngoài ra, nó cho bạn biết rõ ràng sự khác biệt giữa mô hình lệnh và chiến lược nói một cách ngắn gọn - mục đích. "Chương trình chính về cơ bản chọn một chiến lược tổng thể tùy thuộc vào ngày trong tuần"
Utsav T.

7

Tôi biết đây là một câu hỏi cũ, nhưng tôi nghĩ rằng tôi có một ví dụ thú vị khác mà tôi đã thực hiện gần đây.

Đây là một ví dụ rất thực tế về mô hình chiến lược đang được sử dụng trong hệ thống cung cấp tài liệu.

Tôi đã có một hệ thống phân phối PDF nhận được một kho lưu trữ chứa nhiều tài liệu và một số siêu dữ liệu. Dựa trên siêu dữ liệu, nó quyết định vị trí đặt tài liệu vào; nói, tùy thuộc vào dữ liệu, tôi có thể lưu trữ các tài liệu A, Bhoặc Chệ thống lưu trữ, hoặc một kết hợp của ba.

Các khách hàng khác nhau đã sử dụng hệ thống này và họ có các yêu cầu khôi phục / xử lý lỗi khác nhau trong trường hợp có lỗi: một người muốn hệ thống phân phối dừng ở lỗi đầu tiên, để lại tất cả tài liệu đã được giao trong kho của họ, nhưng dừng quá trình và không giao bất kỳ thứ gì khác ; một người khác muốn nó khôi phục Btrong trường hợp có lỗi khi lưu trữ C, nhưng hãy để lại bất cứ thứ gì đã được giao cho A. Thật dễ dàng để tưởng tượng rằng một thứ ba hoặc thứ tư cũng sẽ có những nhu cầu khác nhau.

Để giải quyết vấn đề, tôi đã tạo một lớp phân phối cơ bản có chứa logic phân phối, cùng với các phương thức để khôi phục nội dung từ tất cả các kho. Những phương pháp đó không thực sự được gọi bởi hệ thống phân phối trực tiếp trong trường hợp có lỗi. Thay vào đó, lớp này sử dụng Dependency Injection để nhận một lớp "Rollback / Error Xử lý Chiến lược" (dựa trên khách hàng sử dụng hệ thống), được gọi trong trường hợp có lỗi, lớp này sẽ gọi các phương thức rollback nếu nó phù hợp với chiến lược đó.

Bản thân lớp phân phối báo cáo những gì đang xảy ra với lớp chiến lược (tài liệu nào đã được chuyển đến kho nào và những lỗi nào đã xảy ra) và bất cứ khi nào có lỗi xảy ra, nó sẽ hỏi chiến lược có tiếp tục hay không. Nếu chiến lược cho biết "dừng nó", lớp này sẽ gọi phương thức "cleanUp" của chiến lược, phương thức này sử dụng thông tin được báo cáo trước đó để quyết định phương thức khôi phục nào cần gọi từ lớp phân phối hoặc đơn giản là không làm gì cả.

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

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

Vì vậy, bây giờ tôi có hai chiến lược khác nhau: một là QuitterStrategy(thoát khỏi lỗi đầu tiên và không xóa gì) và một là MaximizeDeliveryToAStrategy(cố gắng hết sức có thể để không hủy bỏ quy trình và không bao giờ khôi phục nội dung được phân phối vào bộ nhớ A, nhưng khôi phục nội dung Bnếu giao hàng Ckhông thành công).

Theo hiểu biết của tôi, đây là một ví dụ về mô hình chiến lược. Nếu bạn (có, bạn đang đọc) nghĩ tôi sai, hãy comment bên dưới cho tôi biết. Tôi tò mò không biết điều gì sẽ tạo nên cách sử dụng "thuần túy" của mẫu chiến lược và những khía cạnh nào trong việc triển khai của tôi vi phạm định nghĩa. Tôi nghĩ nó trông hơi buồn cười vì giao diện chiến lược hơi béo. Tất cả các ví dụ tôi đã thấy cho đến nay chỉ sử dụng một phương pháp, nhưng tôi vẫn nghĩ rằng điều này gói gọn một thuật toán (nếu một phần logic nghiệp vụ có thể được coi là một thuật toán, thì tôi nghĩ nó đúng).

Vì chiến lược cũng được thông báo về các sự kiện trong quá trình thực hiện phân phối, nó cũng có thể được coi là Người quan sát , nhưng đó là một câu chuyện khác.

Thực hiện một nghiên cứu nhỏ, có vẻ như đây là một "mẫu kết hợp" (giống như MVC, một mẫu sử dụng nhiều mẫu thiết kế bên dưới theo một cách cụ thể) được gọi là Cố vấn . Đó là một cố vấn về việc phân phối có nên tiếp tục hay không, nhưng nó cũng là một trình xử lý lỗi hoạt động vì nó có thể khôi phục nội dung khi được yêu cầu.

Tuy nhiên, đây là một ví dụ khá phức tạp có thể khiến bạn cảm thấy rằng cách sử dụng của mô hình chiến lược đều quá đơn giản / ngớ ngẩn. Nó có thể thực sự phức tạp và thậm chí có thể áp dụng hơn khi được sử dụng cùng với các mẫu khác.


6

Mẫu chiến lược là mẫu được sử dụng phổ biến nhất, đặc biệt cho các thuật toán xác thực và sắp xếp.

Hãy để tôi giải thích bằng một ví dụ thực tế đơn giản

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);
 }

}  

Mã kiểm tra cho điều này là

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);
 }

}

Ví dụ tương tự được lấy từ http://coder2design.com/strategy-pattern/


Các cách sử dụng khác nhau của mẫu chiến lược: Xác thực: Khi có rất nhiều xác thực cần được thực hiện trong mã của bạn. Các thuật toán khác nhau: Đặc biệt khi các thuật toán sắp xếp khác nhau có thể được sử dụng, ví dụ như sắp xếp bong bóng hoặc sắp xếp nhanh. Lưu trữ Thông tin: Khi chúng ta có thể thông tin ở những nơi khác nhau, ví dụ như cơ sở dữ liệu hoặc hệ thống tệp. Phân tích cú pháp: trong khi phân tích cú pháp, chúng ta có thể sử dụng chiến lược khác nhau cho các đầu vào khác nhau. Các chiến lược lọc. Chiến lược bố cục.
Jatinder Pal

5

Một ví dụ điển hình về mô hình chiến lược sẽ là trong một trò chơi mà chúng ta có thể có các nhân vật khác nhau và mỗi nhân vật có thể có nhiều vũ khí để tấn công nhưng tại một thời điểm chỉ có thể sử dụng một vũ khí. Vì vậy, chúng ta có nhân vật làm bối cảnh, ví dụ như Vua, Chỉ huy, Hiệp sĩ, Người lính và vũ khí là một chiến lược trong đó tấn công () có thể là phương thức / thuật toán phụ thuộc vào vũ khí được sử dụng. Vì vậy, nếu các lớp vũ khí cụ thể là Sword, Axe, Crossbow, BowAndArrow, v.v. thì tất cả chúng sẽ thực hiện phương thức tấn công (). Tôi chắc chắn rằng không cần giải thích thêm.


1
Tôi nghĩ rằng câu trả lời chấp nhận được cho là để nói về ví dụ này :)
Jeancarlo Fontalvo

2

Tôi đã sử dụng cách tiếp cận chiến lược trong một công cụ khá phức tạp trong một ứng dụng là một ví dụ điển hình. Về cơ bản, vai trò của engine là đi và đầu tiên tìm danh sách những người có một widget, vai trò thứ hai là tìm ra 10 người tốt nhất với một widget dựa trên một số thông số chưa biết (những thứ như khoảng cách giá kinh doanh trước đó cùng nhau , số lượng trong kho, tùy chọn vận chuyển, v.v. vv ...)

Về cơ bản những gì chúng tôi đã làm là chia vấn đề thành hai chiến lược, đầu tiên là truy xuất dữ liệu, vì chúng tôi biết rằng chúng tôi có nhiều nguồn vật dụng của mình và chúng tôi cần có thể lấy dữ liệu và chuyển đổi nó thành một cấu trúc chung.

Sau đó, chúng tôi cũng nhận ra rằng chúng tôi có nhiều biệt danh, một số dựa trên trọng số của các tham số, một số khác thì rất kỳ lạ và mang tính dự đoán và tôi không thể thực hiện chúng một cách công bằng nếu không kéo ra các biểu đồ và biểu đồ và bạn sẽ có được bức tranh, chúng tôi có rất nhiều biệt danh cho lựa chọn những người tốt nhất.

Bản thân dịch vụ của chúng tôi về cơ bản là nó xác định các đầu vào, đầu ra và thực hiện một số chuẩn hóa dữ liệu, nó cũng sử dụng mẫu nhà cung cấp để bổ sung các nhà cung cấp dữ liệu và nhà cung cấp algorithim ứng dụng cụ thể đã sử dụng chiến lược này. Đây là một hệ thống khá hiệu quả.

Chúng tôi đã có một số cuộc tranh luận nếu chúng tôi đang sử dụng một chiến lược hoặc một mô hình mẫu mà chúng tôi chưa bao giờ giải quyết được.


2

Bạn có chắc rằng trạng thái của một "đơn đặt hàng" không phải là một mẫu trạng thái? Tôi có linh cảm rằng một đơn đặt hàng sẽ không được xử lý khác nhau tùy thuộc vào tình trạng của nó.

Lấy ví dụ phương pháp Ship trên theo thứ tự:

order.Ship();
  • Nếu phương thức vận chuyển thay đổi theo chức năng của trạng thái, thì bạn đã có một mẫu chiến lược.
  • Tuy nhiên, nếu phương thức Ship () chỉ thành công khi đơn đặt hàng đã được thanh toán và đơn đặt hàng vẫn chưa được giao, bạn đã có một mẫu trạng thái.

Ví dụ tốt nhất về mô hình trạng thái (và các mẫu khác) mà tôi tìm thấy là trong cuốn sách " Các mẫu thiết kế đầu tiên ", thật tuyệt vời. Một phần hai gần đây sẽ là một loạt các mẫu blog của David Cumps .


2

Giả sử bạn muốn viết một thuật toán để tính X ngày thứ n của một tháng và năm nhất định, ví dụ: Thứ Hai thứ hai của tháng 10 năm 2014. Bạn muốn sử dụng lớp Thời gian của Android android.text.format.Timeđể biểu thị ngày, nhưng bạn cũng muốn viết một thuật toán chung mà cũng có thể áp dụng cho java.util.Calendar.

Đây là những gì tôi đã làm.

Trong DatetimeMath.java:

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

    public int getDayOfWeek(Object datetime);

    public void increment(Object datetime);
}

Trong 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);
    }
}

Trong OrdinalDayOfWeekCalculator.java, lớp có thuật toán chung:

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;
    }
}

Trong ứng dụng Android của tôi, tôi sẽ gọi một cái gì đó như

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

Nếu tôi muốn sử dụng lại cùng một thuật toán java.util.Calendar, tôi chỉ cần viết một lớp CalendarMath triển khai ba phương thức trong DatetimeMath và sau đó sử dụng

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

Một vài tuần trước, tôi đã thêm một giao diện Java chung được triển khai bởi một trong các đối tượng miền của chúng tôi. Đối tượng miền này được tải từ cơ sở dữ liệu và biểu diễn cơ sở dữ liệu là một lược đồ hình sao với khoảng 10 nhánh trở lên. Một trong những hậu quả của việc có một đối tượng miền có trọng lượng nặng như vậy là chúng ta phải tạo các đối tượng miền khác đại diện cho cùng một lược đồ, mặc dù có trọng lượng nhỏ hơn. Vì vậy, tôi đã làm cho các đối tượng nhẹ khác triển khai cùng một giao diện. Nói cách khác, chúng tôi đã có:

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

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

Ban đầu, tôi muốn sử dụng CollectibleElephantđể sắp xếp Elephants. Khá nhanh chóng, các đồng đội của tôi CollectibleElephantbắt đầu chạy kiểm tra bảo mật, lọc chúng khi chúng được gửi đến GUI, v.v.


1

Chúng tôi phải tạo giao diện cung cấp của bên thứ ba cho nền tảng doanh nghiệp với cơ sở dữ liệu rất phức tạp. Việc gửi dữ liệu được cung cấp giống như một danh sách các kiểu dữ liệu của chúng tôi được đưa vào hàng đợi ưu tiên trong ứng dụng của chúng tôi để chúng có thể được ghi vào cơ sở dữ liệu theo đúng thứ tự do phụ thuộc.

Quá trình ghi dữ liệu đó sau đó khá đơn giản, hãy tiếp tục xuất hiện ở đầu hàng đợi ưu tiên và sau đó chọn chiến lược dựa trên loại đối tượng mà bạn trích xuất.


0

Từ wikipedia

Trong lập trình máy tính, mẫu chiến lược (còn được gọi là mẫu chính sách) là một mẫu thiết kế phần mềm hành vi cho phép lựa chọn một thuật toán trong thời gian chạy. Thay vì triển khai trực tiếp một thuật toán duy nhất, mã nhận được các hướng dẫn về thời gian chạy để sử dụng trong nhóm thuật toán

Trong ứng dụng Windows Paint, bạn có thể thấy một mẫu chiến lược trong đó bạn có thể chọn hình dạng và màu sắc một cách độc lập trong các phần khác nhau. Ở đây hình dạng và màu sắc là các thuật toán có thể thay đổi trong thời gian chạy.

Nếu bạn muốn vẽ một hình tròn với màu đỏ, thay vì cung cấp tùy chọn 'RedCircle', họ cho phép bạn chọn hình tròn và màu bạn chọn.

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

Không có mô hình chiến lược sẽ làm tăng số lượng các lớp với tích Descartes về hình dạng và màu sắc. Ngoài ra, giao diện thay đổi cho mỗi lần triển khai.


0

Hãy tưởng tượng một trò chơi bắn súng với kẻ thù AI ​​chẳng hạn. Bạn muốn họ liên tục chiến đấu theo những cách khác nhau dựa trên những gì xảy ra .. Với mô hình chiến lược, bạn có thể liên tục lặp lại và thay đổi động cách thực hiện một hành động hoặc cụ thể.

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());
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.