Learn the design pattern "Strategy" in Python

As a material for learning GoF design patterns, the book "Introduction to Design Patterns Learned in the Augmented and Revised Java Language" seems to be helpful. However, since the examples taken up are based on JAVA, I tried the same practice in Python to deepen my understanding.

■ Strategy pattern (strategy pattern)

The Strategy pattern is a design pattern that allows algorithms to be selected at run time in the area of computer programming. The Strategy pattern holds a reference to the subroutine that describes the algorithm inside the data structure. In addition to function pointers, function objects, and delegates, polymorphism and delegation in orthodox object-oriented languages, or dynamic duck typing by reflection are used to realize this pattern.

UML class and sequence diagram W3sDesign_Strategy_Design_Pattern_UML.jpg

UML class diagram strategy.png (The above is quoted from Wikipedia)

□ Memorandum

The Strategy pattern consciously separates the algorithm part from the other parts and specifies only the interface part with that algorithm. Then, the algorithm is used by delegation from the program. When improving the program, if you are using the Strategy pattern, be careful not to change the interface of the Strategy role, you only need to modify the ConcreteStrategy role, and ** delegation is a loose Because it uses ties, it also makes it easy to switch between algorithms **.

■ "Strategy" sample program

Actually, I would like to run a sample program that utilizes the Strategy pattern and check the following behavior. Here, ** Taro ** and ** Hanako ** will play rock-paper-scissors repeatedly to compete for victory or defeat.


Rock-paper-scissors strategy

――If Taro beats rock-paper-scissors, he will challenge the next rock-paper-scissors with the same hand. --Hanako challenges rock-paper-scissors in the order of "goo", "choki", "par"

$ python Main.py
Even...
Winner:[Hana: 1games,  0win,  0lose]
Winner:[Hana: 2games,  1win,  0lose]
Winner:[Hana: 3games,  2win,  0lose]
Even...
Winner:[Taro: 5games,  0win,  3lose]
Winner:[Hana: 6games,  3win,  1lose]
Winner:[Taro: 7games,  1win,  4lose]
Winner:[Hana: 8games,  4win,  2lose]
Even...

...(snip)

Winner:[Taro: 9976games,  2433win,  5070lose]
Winner:[Hana: 9977games,  5070win,  2434lose]
Winner:[Hana: 9978games,  5071win,  2434lose]
Winner:[Taro: 9979games,  2434win,  5072lose]
Winner:[Hana: 9980games,  5072win,  2435lose]
Even...
Winner:[Hana: 9982games,  5073win,  2435lose]
Even...
Even...
Winner:[Taro: 9985games,  2435win,  5074lose]
Winner:[Hana: 9986games,  5074win,  2436lose]
Winner:[Taro: 9987games,  2436win,  5075lose]
Winner:[Hana: 9988games,  5075win,  2437lose]
Winner:[Taro: 9989games,  2437win,  5076lose]
Winner:[Hana: 9990games,  5076win,  2438lose]
Winner:[Hana: 9991games,  5077win,  2438lose]
Winner:[Taro: 9992games,  2438win,  5078lose]
Winner:[Hana: 9993games,  5078win,  2439lose]
Winner:[Taro: 9994games,  2439win,  5079lose]
Winner:[Hana: 9995games,  5079win,  2440lose]
Winner:[Hana: 9996games,  5080win,  2440lose]
Winner:[Hana: 9997games,  5081win,  2440lose]
Winner:[Hana: 9998games,  5082win,  2440lose]
Even...
Total Result:
[Taro: 10000games,  2440win,  5083lose]
[Hana: 10000games,  5083win,  2440lose]

The result is that the ** hanako ** rock-paper-scissors strategy (challenge the rock-paper-scissors in the order of "goo", "choki", "par") is superior to the ** Taro ** rock-paper-scissors strategy. I did. This is because the ** Taro ** rock-paper-scissors strategy (if you win the rock-paper-scissors, challenge the next rock-paper-scissors with the same hand), you can never win in a row.

■ Details of sample program

Similar code has been uploaded to the Git repository. https://github.com/ttsubo/study_of_design_pattern/tree/master/Strategy

--Directory structure

.
├── Main.py
└── strategy
    ├── __init__.py
    ├── hand.py
    ├── player.py
    └── strategy.py

(1) The role of Strategy

It is the role that defines the interface for using the strategy. In the sample program, the Strategy class serves this role.

strategy/strategy.py


from abc import ABCMeta, abstractmethod

class Strategy(metaclass=ABCMeta):
    @abstractmethod
    def nextHand(self):
        pass

    @abstractmethod
    def study(self, win):
        pass

(2) The role of Concrete Strategy

It is the role that actually implements the interface of the role of Strategy. Here, you will actually program a specific strategy (work, policy, method, algorithm). In the sample program, the WinningStrategy class and the CircularStrategy class serve this role.

strategy/strategy.py


import random
from strategy.hand import Hand

class WinningStrategy(Strategy):
    def __init__(self):
        self.__won = False
        self.__prevHand = None

    def nextHand(self):
        if not self.__won:
            self.__prevHand = Hand.getHand(random.randint(0, 2))
        return self.__prevHand

    def study(self, win):
        self.__won = win

class CircularStrategy(Strategy):
    def __init__(self):
        self.__Hand = 0

    def nextHand(self):
        return Hand.getHand(self.__Hand)

    def study(self, win):
        self.__Hand = (self.__Hand + 1) % 3

(3) The role of Context

It is a role that uses the Strategy role. I have an instance of the ConcreteStrategy role and use it as needed. In the sample program, the Player class serves this role.

strategy/player.py


class Player(object):
    def __init__(self, name, strategy):
        self.__name = name
        self.__strategy = strategy
        self.__wincount = 0
        self.__losecount = 0
        self.__gamecount = 0

    def nextHand(self):
        return self.__strategy.nextHand()

    def win(self):
        self.__strategy.study(True)
        self.__wincount += 1
        self.__gamecount += 1

    def lose(self):
        self.__strategy.study(False)
        self.__losecount += 1
        self.__gamecount += 1

    def even(self):
        self.__gamecount += 1

    def __str__(self):
        return "[{0}: {1}games,  {2}win,  {3}lose]".format(self.__name,
                                                           self.__gamecount,
                                                           self.__wincount,
                                                           self.__losecount)

(4) The role of Client

In the sample program, the startMain method serves this role.

Main.py


import sys
import strategy.strategy
from strategy.strategy import WinningStrategy, CircularStrategy
from strategy.player import Player

def startMain():
    player1 = Player("Taro", WinningStrategy())
    player2 = Player("Hana", CircularStrategy())

    for _ in range(10000):
        nextHand1 = player1.nextHand()
        nextHand2 = player2.nextHand()
        if nextHand1.isStrongerThan(nextHand2):
            print("Winner:{0}".format(player1))
            player1.win()
            player2.lose()
        elif nextHand2.isStrongerThan(nextHand1):
            print("Winner:{0}".format(player2))
            player1.lose()
            player2.win()
        else:
            print("Even...")
            player1.even()
            player2.even()

    print("Total Result:")
    print(player1)
    print(player2)

if __name__ == '__main__':
    startMain()

(5) Other

Manage the victory or defeat of rock-paper-scissors.

strategy/hand.py


class Hand(object):
    HANDVALUE_GUU = 0
    HANDVALUE_CHO = 1
    HANDVALUE_PAA = 2
    name = ["Goo", "Choki", "Par"]
    hands = []

    def __init__(self, handvalue):
        self.__handvalue = handvalue

    @classmethod
    def getHand(cls, handvalue):
        return cls.hands[handvalue]

    def isStrongerThan(self, hand):
        return self.fight(hand) == 1

    def isWeakerThan(self, hand):
        return self.fight(hand) == -1

    def fight(self, hand):
        if self == hand:
            return 0
        elif (self.__handvalue + 1) % 3 == hand.__handvalue:
            return 1
        else:
            return -1

#    def toString(self):
#        return self.name[self.__handvalue]


Hand.hands.append(Hand(Hand.HANDVALUE_GUU))
Hand.hands.append(Hand(Hand.HANDVALUE_CHO))
Hand.hands.append(Hand(Hand.HANDVALUE_PAA))

■ Reference URL

-[Finishing "Introduction to Design Patterns Learned in Java Language" (Not)](https://medium.com/since-i-want-to-start-blog-that-looks-like-men-do/java Introduction to Design Patterns Learned in Language-Finishing-Not-2cc9b34a30b2)

Recommended Posts

Learn the design pattern "Strategy" in Python
Learn the design pattern "Prototype" in Python
Learn the design pattern "Builder" in Python
Learn the design pattern "Observer" in Python
Learn the design pattern "Proxy" in Python
Learn the design pattern "Command" in Python
Learn the design pattern "Visitor" in Python
Learn the design pattern "Bridge" in Python
Learn the design pattern "Mediator" in Python
Learn the design pattern "Decorator" in Python
Learn the design pattern "Composite" in Python
Learn the design pattern "State" in Python
Learn the design pattern "Adapter" in Python
Learn the design pattern "Abstract Factory" in Python
Learn the design pattern "Template Method" in Python
Learn the design pattern "Factory Method" in Python
Learn the design pattern "Chain of Responsibility" in Python
Learn the design pattern "Singleton" with Python
Learn the design pattern "Facade" with Python
Implement the Singleton pattern in Python
Singleton pattern in Python
Download the file in Python
Learn cumulative sum in Python
Design Patterns in Python: Introduction
Thorough logging strategy in Python
Learn exploration in Python # 1 Full exploration
Python Design Pattern --Template method
Getting the arXiv API in Python
Python in the browser: Brython's recommendation
Save the binary file in Python
Get the desktop path in Python
Get the script path in Python
In the python command python points to python3.8
I wrote the queue in Python
Calculate the previous month in Python
Examine the object's class in python
Get the desktop path in Python
Get the host name in Python
Access the Twitter API in Python
The first step in Python Matplotlib
I wrote the stack in Python
Master the weakref module in Python
Learn the basics of Python ① Beginners
Load the remote Python SDK in IntelliJ
Try using the Wunderlist API in Python
Check the behavior of destructor in Python
[Python Kivy] About changing the design theme
Try using the Kraken API in Python
Write the test in a python docstring
OR the List in Python (zip function)
GoF design pattern from the problem 2. Structure
Display Python 3 in the browser with MAMP
Tweet using the Twitter API in Python
Check if the URL exists in Python
Run the Python interpreter in a script
The result of installing python in Anaconda
What is "mahjong" in the Python library? ??
Read the file line by line in Python
Read the file line by line in Python
GoF design pattern from the problem 1. Generation
MongoDB for the first time in Python