2009-06-08 40 views
34

Trong mục nhập năm 2009 cho Mẫu Chiến lược, có một ví dụ written in PHP.Làm thế nào để viết mẫu chiến lược bằng Python khác với ví dụ trong Wikipedia?

Hầu hết các mẫu mã khác làm điều gì đó như:

a = Context.new(StrategyA.new) 
a.execute #=> Doing the task the normal way 

b = Context.new(StrategyB.new) 
b.execute #=> Doing the task alternatively 

c = Context.new(StrategyC.new) 
c.execute #=> Doing the task even more alternative 

Trong đoạn mã Python một kỹ thuật khác nhau được sử dụng với một nút Submit. Tôi tự hỏi mã Python sẽ trông như thế nào nếu nó cũng giống như cách các mẫu mã khác thực hiện.

Cập nhật: Nó có thể ngắn hơn bằng các hàm hạng nhất trong Python không?

Trả lời

56

Ví dụ trong Python không quá khác biệt so với các ví dụ khác. Để thử kịch bản PHP:

class StrategyExample: 
    def __init__(self, func=None): 
     if func: 
      self.execute = func 

    def execute(self): 
     print("Original execution") 

def executeReplacement1(): 
    print("Strategy 1") 

def executeReplacement2(): 
    print("Strategy 2") 

if __name__ == "__main__": 
    strat0 = StrategyExample() 
    strat1 = StrategyExample(executeReplacement1) 
    strat2 = StrategyExample(executeReplacement2) 

    strat0.execute() 
    strat1.execute() 
    strat2.execute() 

Output:

Original execution 
Strategy 1 
Strategy 2 

Sự khác biệt chính là:

  • Bạn không cần phải viết bất kỳ lớp khác hoặc thực hiện bất kỳ giao diện.
  • Thay vào đó, bạn có thể chuyển tham chiếu hàm sẽ được ràng buộc với phương pháp bạn muốn.
  • Các chức năng vẫn có thể được sử dụng riêng và đối tượng gốc có thể có hành vi mặc định nếu bạn muốn (mẫu if func == None có thể được sử dụng cho điều đó).
  • Thật vậy, nó gọn gàng và thanh lịch như bình thường với Python. Nhưng bạn mất thông tin; không có giao diện rõ ràng, lập trình viên được giả định là người lớn để biết họ đang làm gì.

Lưu ý rằng có 3 cách để tự động thêm một phương pháp trong Python:

  • Con đường tôi đã cho các bạn. Nhưng phương pháp sẽ tĩnh, nó sẽ không nhận được đối số "tự".

  • Sử dụng tên lớp:

    StrategyExample.execute = func

Ở đây, tất cả các trường hợp sẽ nhận được func như phương pháp execute, và sẽ nhận được self thông qua như là một cuộc tranh cãi.

  • Ràng buộc với một trường hợp duy nhất (sử dụng các mô-đun types):

    strat0.execute = types.MethodType(executeReplacement1, strat0)

    hoặc với Python 2, lớp của các trường hợp được thay đổi cũng được yêu cầu:

    strat0.execute = types.MethodType(executeReplacement1, strat0, StrategyExample)

Điều này sẽ bi nd phương pháp mới cho strat0 và chỉ strat0, giống như ví dụ đầu tiên. Nhưng start0.execute() sẽ nhận được self được chuyển làm đối số.

Nếu bạn cần sử dụng tham chiếu đến cá thể hiện tại trong hàm, thì bạn sẽ kết hợp phương thức đầu tiên và phương thức cuối cùng. Nếu bạn không:

class StrategyExample: 
    def __init__(self, func=None): 
     self.name = "Strategy Example 0" 
     if func: 
      self.execute = func 

    def execute(self): 
     print(self.name) 

def executeReplacement1(): 
    print(self.name + " from execute 1") 

def executeReplacement2(): 
    print(self.name + " from execute 2") 

if __name__ == "__main__": 
    strat0 = StrategyExample() 
    strat1 = StrategyExample(executeReplacement1) 
    strat1.name = "Strategy Example 1" 
    strat2 = StrategyExample(executeReplacement2) 
    strat2.name = "Strategy Example 2" 

    strat0.execute() 
    strat1.execute() 
    strat2.execute() 

Bạn sẽ nhận được:

Traceback (most recent call last): 
    File "test.py", line 28, in <module> 
    strat1.execute() 
    File "test.py", line 13, in executeReplacement1 
    print self.name + " from execute 1" 
NameError: global name 'self' is not defined 

Vì vậy, các mã đúng sẽ là:

import sys 
import types 

if sys.version_info[0] > 2: # Python 3+ 
    create_bound_method = types.MethodType 
else: 
    def create_bound_method(func, obj): 
     return types.MethodType(func, obj, obj.__class__) 

class StrategyExample: 
    def __init__(self, func=None): 
     self.name = "Strategy Example 0" 
     if func: 
      self.execute = create_bound_method(func, self) 

    def execute(self): 
     print(self.name) 

def executeReplacement1(self): 
    print(self.name + " from execute 1") 

def executeReplacement2(self): 
    print(self.name + " from execute 2") 

if __name__ == "__main__": 
    strat0 = StrategyExample() 
    strat1 = StrategyExample(executeReplacement1) 
    strat1.name = "Strategy Example 1" 
    strat2 = StrategyExample(executeReplacement2) 
    strat2.name = "Strategy Example 2" 

    strat0.execute() 
    strat1.execute() 
    strat2.execute() 

chí này ra kết quả mong đợi:

Strategy Example 0 
Strategy Example 1 from execute 1 
Strategy Example 2 from execute 2 

Tất nhiên, trong trường hợp các chức năng không thể được sử dụng độc lập nữa, nhưng vẫn có thể bị ràng buộc với bất kỳ cá thể nào khác của bất kỳ đối tượng nào, mà không có bất kỳ giới hạn giao diện nào.

8

Để rõ ràng, tôi vẫn sẽ sử dụng một giao diện giả:

class CommunicationStrategy(object): 
    def execute(self, a, b): 
     raise NotImplementedError('execute') 

class ConcreteCommunicationStrategyDuck(CommunicationStrategy): 
    def execute(self, a, b): 
     print "Quack Quack" 

class ConcreteCommunicationStrategyCow(CommunicationStrategy): 
    def execute(self, a, b): 
     print "Mooo" 

class ConcreteCommunicationStrategyFrog(CommunicationStrategy): 
    def execute(self, a, b): 
     print "Ribbit! Ribbit!" 
+0

Đây là mẫu mẫu, không phải là chiến lược – Twisty

29

Bạn nói đúng, ví dụ wikipedia là không hữu ích. Nó conflates hai điều.

  1. Chiến lược.

  2. Tính năng của Python giúp đơn giản hóa việc triển khai Chiến lược. Câu lệnh "không cần triển khai mẫu này một cách rõ ràng" là không chính xác. Bạn thường cần phải thực hiện Chiến lược, nhưng Python đơn giản hóa điều này bằng cách cho phép bạn sử dụng một hàm mà không có phí của trình bao bọc lớp xung quanh một hàm.

Đầu tiên, Chiến lược.

class AUsefulThing(object): 
    def __init__(self, aStrategicAlternative): 
     self.howToDoX = aStrategicAlternative 
    def doX(self, someArg): 
     self. howToDoX.theAPImethod(someArg, self) 

class StrategicAlternative(object): 
    pass 

class AlternativeOne(StrategicAlternative): 
    def theAPIMethod(self, someArg, theUsefulThing): 
     pass # an implementation 

class AlternativeTwo(StrategicAlternative): 
    def theAPImethod(self, someArg, theUsefulThing): 
     pass # another implementation 

Bây giờ bạn có thể làm những việc như thế này.

t = AUsefulThing(AlternativeOne()) 
t.doX(arg) 

Và nó sẽ sử dụng đối tượng chiến lược mà chúng tôi đã tạo.

Thứ hai, các giải pháp thay thế Python.

class AUsefulThing(object): 
    def __init__(self, aStrategyFunction): 
     self.howToDoX = aStrategyFunction 
    def doX(self, someArg): 
     self.howToDoX(someArg, self) 

def strategyFunctionOne(someArg, theUsefulThing): 
     pass # an implementation 

def strategyFunctionTwo(someArg, theUsefulThing): 
     pass # another implementation 

Chúng tôi có thể thực hiện việc này.

t= AUsefulThing(strategyFunctionOne) 
t.doX(anArg) 

Điều này cũng sẽ sử dụng chức năng chiến lược mà chúng tôi cung cấp.

+0

bạn sử dụng mẫu nào? Bạn sẽ khuyên bạn nên sử dụng cái gì? – Escualo

+3

@Arrieta: Sử dụng cả hai. Đề nghị cả hai. Họ có các ứng dụng khác nhau. Nếu * Chiến lược * có nhiều phương thức hoặc là (bằng cách nào đó) có trạng thái, thì bạn cần đầu tiên. Trong trường hợp điển hình hơn, thứ hai hoạt động tốt. Nếu bạn đang làm việc với rất nhiều lập trình viên Java hoặc C++, bạn phải sử dụng đầu tiên bởi vì người thứ hai lẫn lộn chúng. –

35

Trả lời một câu hỏi cũ cho nhân viên của Google, người đã tìm kiếm "mô hình chiến lược trăn" và hạ cánh ở đây ...

mẫu Đây là thực tế không tồn tại trong các ngôn ngữ có hỗ trợ chức năng hạng nhất. Bạn có thể muốn xem xét việc tận dụng tính năng này bằng Python:

def strategy_add(a, b): 
    return a + b 

def strategy_minus(a, b): 
    return a - b 

solver = strategy_add 
print solver(1, 2) 
solver = strategy_minus 
print solver(2, 1) 

Cách tiếp cận này rất sạch sẽ và đơn giản.

Ngoài ra, hãy chắc chắn kiểm tra Joe Gregorio PyCon 2009 nói về Python và thiết kế mẫu (hoặc thiếu): http://pyvideo.org/video/146/pycon-2009--the--lack-of--design-patterns-in-pyth

+1

Vậy tại sao chúng ta có các lớp trong python? đôi khi bạn thiết kế các mẫu hữu ích trong các dự án lớn, bạn không thể thoát khỏi nó :) – vivek

+2

Làm việc [link] (http://pyvideo.org/video/146/pycon-2009--the--lack-of- -design-patterns-in-pyth). Trong video khác bị xóa. –

+0

@vivek Một không có các lớp trong python hoặc bất kỳ ngôn ngữ nào khác đặc biệt để tạo điều kiện cho các mẫu thiết kế. Nhiều thứ được đại diện bởi các lớp. Tuy nhiên, yêu cầu các lớp đại diện cho ví dụ: lệnh hoặc mô hình chiến lược là điểm yếu. Bất kỳ ngôn ngữ nào có đóng cửa thích hợp đều có thể thực hiện mà không cần. Và các dự án lớn không phải là một yêu cầu cho các lớp học cụ thể. Rất nhiều ngôn ngữ không có lớp học nào cả và được sử dụng để tạo ra các dự án lớn. Tất nhiên các lớp học là cách duy nhất để thể hiện các kiểu dữ liệu trong Python, do đó bạn không thể thoát khỏi chúng. –

0

tôi đã cố gắng để chuyển đổi 'vịt' ví dụ từ chương 1 (bao gồm Chiến lược Mẫu) của Head First Design Pattern bằng Python:

class FlyWithRocket(): 
    def __init__(self): 
     pass 
    def fly(self): 
     print 'FLying with rocket' 

class FlyWithWings(): 
    def __init__(self): 
     pass 
    def fly(self): 
     print 'FLying with wings' 

class CantFly(): 
    def __init__(self): 
     pass 
    def fly(self): 
     print 'I Cant fly' 

class SuperDuck: 
    def __init__(self): 
     pass 
    def setFlyingBehaviour(self, fly_obj): 
     self.fly_obj = fly_obj 
    def perform_fly(self): 
     self.fly_obj.fly() 

if __name__ == '__main__': 
    duck = SuperDuck() 
    fly_behaviour = FlyWithRocket() 
    #fly_behaviour = FlyWithWings() 
    duck.setFlyingBehaviour(fly_behaviour) 
    duck.perform_fly() 
+0

Nó sẽ là tốt hơn để thiết lập hành vi bay trong constructor hoặc ít nhất là đặt một giá trị mặc định trong constructor. Bạn có thể quên đặt nó và nhận lỗi khác. –

Các vấn đề liên quan