-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat($demo): support strategy design pattern in Python
- Loading branch information
1 parent
c1909ba
commit 2d33e6e
Showing
2 changed files
with
98 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
from loguru import logger | ||
|
||
|
||
class BaseStrategy: | ||
_strategies: list["BaseStrategy"] = [] | ||
|
||
@classmethod | ||
def init(cls) -> None: | ||
if len(cls._strategies) > 0: | ||
return | ||
strategy_classes = BaseStrategy.__subclasses__() | ||
cls._strategies = [ | ||
strategy_class.__new__(strategy_class) | ||
for strategy_class in strategy_classes | ||
] | ||
|
||
def strategy_name(self) -> str: | ||
return self.__class__.__name__ | ||
|
||
def matches(self, data_input: str) -> bool: | ||
raise NotImplementedError( | ||
f"Function `{self.matches.__qualname__}` is not implemented" | ||
) | ||
|
||
def execute(self, data_input: str) -> None: | ||
raise NotImplementedError( | ||
f"Function `{self.execute.__qualname__}` is not implemented" | ||
) | ||
|
||
@classmethod | ||
def iter_execute(cls, data_input: str) -> None: | ||
strategies = list(filter(lambda x: x.matches(data_input), cls._strategies)) | ||
if len(strategies) != 1: | ||
raise ValueError("No supported strategies found") | ||
strategies[0].execute(data_input) | ||
|
||
|
||
class StrategyOne(BaseStrategy): | ||
def matches(self, data_input: str) -> bool: | ||
return "one" in data_input | ||
|
||
def execute(self, data_input: str) -> None: | ||
logger.info(f"Executing {self.strategy_name()} with: {data_input}") | ||
|
||
|
||
class StrategyTwo(BaseStrategy): | ||
def matches(self, data_input: str) -> bool: | ||
return "two" in data_input | ||
|
||
def execute(self, data_input: str) -> None: | ||
logger.info(f"Executing {self.strategy_name()} with: {data_input}") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import pytest | ||
from loguru import logger | ||
|
||
from python_boilerplate.demo.strategy_design_pattern_usage import BaseStrategy | ||
|
||
|
||
@pytest.fixture(scope="session", autouse=True) | ||
def setup() -> None: | ||
logger.info("Initializing tests...") | ||
BaseStrategy.init() | ||
|
||
|
||
def test_strategies_when_matches() -> None: | ||
BaseStrategy.iter_execute("Data for strategy one") | ||
BaseStrategy.iter_execute("Data for strategy two") | ||
|
||
|
||
def test_strategies_when_not_matches() -> None: | ||
with pytest.raises(ValueError) as exc_info: | ||
BaseStrategy.iter_execute("Data that doesn't match strategy") | ||
assert exc_info is not None | ||
logger.warning(f"Expected exception: {exc_info.value}") | ||
|
||
|
||
def test_base_strategy() -> None: | ||
strategy = BaseStrategy() | ||
strategy_name = strategy.strategy_name() | ||
assert strategy_name is not None | ||
assert strategy_name == BaseStrategy.__name__ | ||
with pytest.raises(NotImplementedError) as exc_info_for_matches: | ||
strategy.matches("any") | ||
assert exc_info_for_matches is not None | ||
with pytest.raises(NotImplementedError) as exc_info_for_execute: | ||
strategy.execute("any") | ||
assert exc_info_for_execute is not None | ||
|
||
|
||
def test_call_twice_init() -> None: | ||
try: | ||
BaseStrategy.init() | ||
BaseStrategy.init() | ||
strategies = BaseStrategy._strategies | ||
assert strategies is not None | ||
assert len(strategies) == 2 | ||
logger.info(f"Strategies: {strategies}") | ||
except Exception as e: | ||
assert False, f"Unexpected exception raised: {e}" |