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.
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
UML class diagram (The above is quoted from Wikipedia)
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 **.
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.
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
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
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
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)
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()
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))
-[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