Python策略模式介绍、使用方法

一、Python策略模式介绍

Python策略模式(Strategy Pattern)是一种软件设计模式,用于通过将算法封装为独立的对象,而使得它们可以在运行时动态地相互替换。该模式使得算法的变化独立于使用它们的客户端,从而达到代码的可扩展性、灵活性和可维护性。

功能:

1.将不同算法进行抽象和封装,使得它们可以互相替换,从而增强程序的可扩展性。

2.将算法的变化独立于客户端,使得客户端代码不需要修改即可使用不同的算法。

3.提高程序的可读性和可维护性。

优点:

1.代码可扩展性和灵活性好,能够适应不同的需求。

2.降低模块之间的耦合度,提高代码的可维护性和可读性。

3.具有良好的可扩展性,可以动态地增加、删除或替换算法。

缺点:

1.会增加一定的复杂度,需要额外的类定义。

2.可能会导致系统中出现较多的类,增加系统的复杂度。

3.需要客户端了解不同策略的差异,才能选择合适的策略。

应用场景:

1.需要在运行时根据不同情况选择不同算法的场景。

2.需要封装业务逻辑的场景。

3.需要对同一种算法进行多次修改的场景。

使用方式:

1.抽象出一个策略接口,定义策略的方法。

2.将不同的算法分别封装为具体的策略类,并实现策略接口的方法。

3.创建一个策略上下文类,负责调用不同的策略,根据不同的需求选择合适的策略。

在应用程序开发中的应用:

1.实现排序算法,将排序逻辑抽象出来,将不同的排序算法封装为不同的策略,然后在实现排序的过程中,动态地选择不同的排序算法。

2.实现搜索算法,将搜索算法封装为不同的策略,然后在实现搜索的过程中,动态地选择不同的搜索算法。

二、策略模式使用

工作原理:

1.抽象策略类:定义了一个公共接口,用于所有策略类的实现。

2.具体策略类:具体实现了策略接口的方法。

3.策略上下文类:负责调用具体策略类的实例,根据用户的需求选择相应的策略进行调用。

示例一:实现不同促销活动

假设有一个电商平台,需要实现多种促销活动来吸引用户购买商品。不同的促销活动有不同的算法,例如:满减、折扣、赠品等。这种情况下,可以使用Python策略模式来实现。

首先,定义一个促销策略接口PromotionStrategy,定义促销活动的方法do_promotion

然后,定义具体的促销策略类,例如:满减、折扣、赠品等。这里以满减和折扣为例:

接下来,定义促销上下文类PromotionContext,负责调用不同的策略:

最后,可以在客户端代码中动态地选择不同的促销策略:

# 定义促销策略接口
class PromotionStrategy():
    # 定义促销活动
    def do_promotion(self, price):
        pass

# 定义具体促销策略
class ReductionPromotion(PromotionStrategy):# 满减
    def do_promotion(self, price):
        if price >= 200:
            return price -50
        return price

class DiscountPromotion(PromotionStrategy): # 折扣
    def do_promotion(self, price):
        return price * 0.8

# 定义上下文类,负责调用不同的促销策略
class PromotionContext():
    def __init__(self, promotion_strategy:PromotionStrategy):
        self._promotion_strategy = promotion_strategy

    def execute_promotion_strategy(self, price):
        return self._promotion_strategy.do_promotion(price)


# 动态选择不同促销策略
promotion_type = "Reduction"
promotion_type = "Discount"

if promotion_type == "Reduction":
    promotion_strategy = ReductionPromotion()
elif promotion_type == "Discount":
    promotion_strategy = DiscountPromotion()

promotion_context = PromotionContext(promotion_strategy)
final_price = promotion_context.execute_promotion_strategy(100)
print(final_price)


promotion_type = "Reduction"
# promotion_type = "Discount"

if promotion_type == "Reduction":
    promotion_strategy = ReductionPromotion()
elif promotion_type == "Discount":
    promotion_strategy = DiscountPromotion()

promotion_context = PromotionContext(promotion_strategy)
final_price = promotion_context.execute_promotion_strategy(200)
print(final_price)

运行结果:

 80.0
150

上述代码中,客户端代码通过promotion_type参数来选择不同的促销策略。然后将选择的策略传递给PromotionContext类,由它来负责调用相应的促销策略。最终获得商品的最终价格final_price。如果需要增加或删除促销活动,只需增加或删除相应的促销策略类即可,不需要修改客户端代码。

示例二:实现不同搜索算法

假设我们要实现一个搜索引擎,支持多种搜索算法,例如二分查找、线性查找、哈希查找等。为了实现这个功能,我们可以使用策略模式,将不同的搜索算法实现抽象成不同的类,然后在运行时动态地将它们传递到搜索引擎中。具体的实现如下:


# 定义算法类的抽象基类
class SearchAlgorithm():
    def search(self, array, target):
        pass

# 定义算法:二分法
class BinarySearch(SearchAlgorithm):
    def search(self, array, target):
        left, right = 0,len(array) - 1 # 初始化二分法查找的左右边界,此处0, 9。 相当于:left=0, right=len(array)-1
        while left <= right:
            mid = (left + right) // 2
            if array[mid] == target:
                return mid # 返回查找结果
            elif array[mid] < target:
                left = mid + 1
            else:
                right = mid -1
        return -1

# 定义算法:线性查找
class LineSearch(SearchAlgorithm):
    def search(self, array, target):
        for i in range(len(array)):
            if array[i] == target:
                return i
        return -1

# 定义搜索引擎
class SearchEngine():
    def __init__(self, search_algorithm):
        self.search_algorithm = search_algorithm

    def set_search_algorithm(self,search_algorithm):
        self.search_algorithm = search_algorithm

    def search(self,array, target):
        index = self.search_algorithm.search(array, target) # 调用具体的搜索方法
        if index != -1:
            print(f"Found {target} at index {index}")
        else:
            print(f"{target} not found in the array.")

# 测试代码
array = [1,2,3,4,5,6,7,8,9,10]
target = 5

binary_search = BinarySearch()
line_search = LineSearch()

search_engine = SearchEngine(binary_search)
search_engine.search(array, target)

search_engine = SearchEngine(line_search)
search_engine.search(array, target)

运行结果:

Found 5 at index 4
Found 5 at index 4

在上面的例子中,我们定义了一个SearchAlgorithm类,它是所有搜索算法类的抽象基类。然后,我们定义了两个具体的搜索算法类BinarySearchLinearSearch,它们分别实现了二分查找和线性查找算法。最后,我们定义了一个SearchEngine类,它接收一个具体的搜索算法类,并将其存储在search_algorithm属性中。SearchEngine类还提供了search方法,用于执行搜索操作。

在测试代码中,我们首先创建了一个BinarySearch对象,并用它来创建一个SearchEngine对象。然后,我们调用search方法执行搜索操作,结果显示目标值5被找到了。接着,我们将search_algorithm属性设置为LinearSearch对象,并再次调用search方法,这次结果显示目标值5同样被找到了。

通过使用策略模式,我们可以在运行时根据需要切换搜索算法,而不需要修改搜索引擎的实现代码。这样就使得程序更加灵活和易于维护。

left, right = 0, len(array) - 1

这行代码用于初始化二分查找的左右边界。在列表中查找某个元素时,我们通常要在列表中的一段区间内进行查找。一开始,这个区间通常是整个列表。那么,左右边界要怎么确定呢?

对于有序列表,我们很容易想到的是将整个列表均匀地划分成两半,然后查看中间位置的元素与目标元素的大小关系。如果中间位置的元素等于目标元素,就返回对应的索引;如果中间位置的元素小于目标元素,就在右半边继续查找;如果中间位置的元素大于目标元素,就在左半边继续查找。这个过程就是二分查找。

而对于二分查找算法,我们需要用leftright两个变量来表示当前查找区间的左右边界。一开始,整个列表就是我们要查找的区间,所以left的初始值为0,right的初始值为列表长度减1。这样,在每次查找时,我们就可以根据leftright的值来确定当前查找区间的范围。

定义类时带括号和不带括号区别

在Python中,定义类时可以带括号,也可以不带括号。

语法格式为:

class className:
    # some code here

# 或者

class className():
    # some code here

带括号和不带括号的区别在于继承的方式不同。在Python 2.x版本中,带括号和不带括号的定义方式有区别,带括号的类定义方式是旧式类,不带括号的类定义方式是新式类。而在Python 3.x版本中,带括号和不带括号定义方式没有区别。

新式类和旧式类的主要区别在于继承方式不同。在旧式类中,如果没有显式地指定一个特定的父类,那么Python会默认继承由内置类型object派生出的类;而在新式类中,如果没有显式地指定一个特定的父类,那么Python会默认继承内置类型object

因此,如果需要在Python 2.x版本中使用新式类,建议在定义类时使用带括号的方式。在Python 3.x版本中,则可以使用带括号或不带括号的方式定义类,两种方式等效。

默认继承内置类型object

在Python中,如果没有显式地指定一个特定的父类,那么Python会默认继承内置类型object。下面是一个例子:

class MyClass:
    pass

print(type(MyClass))  # <class 'type'>
print(type(MyClass()))  # <class '__main__.MyClass'>

在上面的例子中,我们定义了一个空的类MyClass,并没有指定父类。我们可以通过调用type()函数来查看MyClass的类型以及用MyClass()创建的实例的类型,都是type__main__.MyClass,这说明Python默认继承了内置类型object

如果我们明确指定了一个父类,那么Python就会直接继承这个父类,例如:

class MyBaseClass:
    pass

class MyClass(MyBaseClass):
    pass

print(type(MyClass))  # <class 'type'>
print(type(MyClass()))  # <class '__main__.MyClass'>

在上面的例子中,我们定义了一个空的父类MyBaseClass,并让MyClass类继承自MyBaseClass。此时,MyClass的类型仍然是type,但是MyClass()创建的实例的类型变为了__main__.MyClass,这说明MyClass类已经从MyBaseClass类继承了一些属性和方法。

猜你喜欢

转载自blog.csdn.net/songpeiying/article/details/131939340