1 设计目标
此篇为实现篇下,如果对策略者模式不熟,可以看知识点篇上。
实现要求:鸭子共有三种行为,游泳行为(所有鸭子都会),飞行行为(能飞/不能飞/具有火箭动力的飞行),叫声行为(嘎嘎叫/吱吱叫/什么都不会叫),不同具体鸭子(绿头鸭/模型鸭/…)飞行行为和叫声行为可能不一样。
2 Java
2.1 涉及知识点
- 接口(interface):定义一种方法约束
- 继承(extend):子类会具有父类的一般特性也会具有自身的特性,提高代码复用性。
区别:
修饰符不同:接口使用interface修饰,继承使用extends修饰。
多继承方式不同:接口支持多继承,继承只支持单继承。
定义方式不同:接口只能定义全局常量和抽象方法,继承可以定义属性、方法、变量、常量等。
作用不同:接口是一种约束,类实现接口就一定要实现它所有的方法;继承是一种扩展,继承了类,它的私有成员以外的成员变量和成员方法子类都将拥有。
- 组合(委托):把A类作为B类的成员变量,具体实现在A类中,B类对外暴露调用接口,解耦合。
- 抽象基类:不能实例化,只能作为类模版,子类需要实现起抽象方法。
- 构造函数:类的初始化方法。
2.2 实现
2.2.1 实现两个接口飞行为和叫行为
public interface FlyBehavior {
public void fly();
}
public interface QuackBehavior {
public void quack();
}
2.2.2 实现Duck抽象基类(把行为接口作为类成员)
package com.jiangxue.strategy;
public abstract class Duck {
//组合(委托):策略类作为抽象基类的成员变量
QuackBehavior quackBehavior;
FlyBehavior flyBehavior;
//定义抽象方法:子类必须要实现
public abstract void display();
//调用类成员的方法
public void performQuack(){
quackBehavior.quack();;
}
public void performFly(){
flyBehavior.fly();
}
//所有子类都有的共有行为
public void swim(){
System.out.println("All ducks float, even decoys");
}
//setter方法设置鸭子行为(运行时改变行为)
public void setFlyBehavior(FlyBehavior fb){
flyBehavior = fb;
}
public void setQuackBehavior(QuackBehavior quackBehavior) {
this.quackBehavior = quackBehavior;
}
}
2.2.3 实现接口飞行为和叫行为的具体行为
- 具体实现飞行为接口:
public class FlyWithWings implements FlyBehavior{
@Override
public void fly(){
System.out.println("I'm flying");
}
}
public class FlyNoWay implements FlyBehavior{
@Override
public void fly() {
System.out.println("I can't fly");
}
}
public class FlyRocketPowered implements FlyBehavior{
@Override
public void fly() {
System.out.println("I'm fly with rocket!");
}
}
- 具体实现叫行为接口:
public class Quack implements QuackBehavior{
@Override
public void quack() {
System.out.println("Quack");
}
}
public class MuteQuack implements QuackBehavior{
@Override
public void quack() {
System.out.println("<<Silence>>");
}
}
public class Squeak implements QuackBehavior{
@Override
public void quack() {
System.out.println("Squack");
}
}
2.2.4 具体实现鸭子
- 实现绿头鸭
public class MallardDuck extends Duck{
//重新构造函数
public MallardDuck(){
quackBehavior = new Quack();
flyBehavior = new FlyWithWings();
}
public void display(){
System.out.println("I'm a real Mallard duck");
}
}
- 实现模型鸭:
public class ModelDuck extends Duck{
public ModelDuck(){
flyBehavior = new FlyNoWay();
quackBehavior = new Quack();
}
@Override
public void display() {
System.out.println("I'm a model duck");
}
}
2.2.5 模型调用
public class Test {
public static void main(String[] args) {
//示例1
Duck mallardDuck = new MallardDuck();
mallardDuck.performQuack();
mallardDuck.performFly();
//示例2
Duck model = new ModelDuck();
model.performFly();
//改变飞行行为
model.setFlyBehavior(new FlyRocketPowered());
model.performFly();
; }
}
3 C++(用到了大量C++2.0的知识)
3.1 涉及知识点
- 纯虚函数(用于实现接口)【virtua和= 0】:在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。有纯虚函数的类是抽象类,不能生成对象,只能派生。
- virtual析构函数:当父类指针指向子类对象时,执行释放操作,子类对象也会被释放掉。
- explicit:该构造函数只能显式调用,禁止隐式转换。
- &&(右值引用):结合了移动语义和完美转发;可以绑定到临时对象。
- 移动语义:不复制对象,而只是让新的对象名指向原对象名所指对象,并且将原对象所指的置空。
- 完美转发:可以撰写接受任意参数的函数模版,并将其转发到其他函数,目标函数会接受与其转发函数所接受的完全相同的参数。
- std::move(把实参强制转换为右值):无条件地右值类型的强制类型转换;
- std::forward:仅对绑定到右值的引用实施向右值类型的强制转换。
- 针对右值引用实施std::move,万能引用实施std::forward
- 智能指针unique_ptr:独占对象,不允许复制;动态管理分配对象的生命周期。
- make_unique函数:动态分配指定类型的内存,并返回一个指向分配内存的唯一指针。
- 子类继承父类的构造:不能直接从初始化列表初始化基类成员,应该使用父类的构造函数。
- 公有继承:基类的公有成员和保护成员的访问属性在派生类中不变
3.2 实现
3.2.1 实现飞行为和叫行为接口
class QuackBehavior{
public:
//纯虚函数只有函数的名字而不具备函数的功能,不能被调用,要求任何派生类都要定义自己的实现方法
//有纯虚函数的类是抽象类,不能生成对象,只能派生。
virtual void quack() const = 0;
//virtual析构函数好处:当父类指针指向子类对象时,执行释放操作,子类对象也会被释放掉
virtual ~QuackBehavior() =default;
};
class FlyBehavior{
public:
virtual void fly() const = 0;
virtual ~FlyBehavior() = default;
};
3.2.2 实现Duck基类(使用委托/组合,把指向抽象接口类的指针作为类成员)
#include <memory>
#include "FlyBehavior.h"
#include "QuackBehavior.h"
#include <iostream>
class Duck {
protected:
//智能指针unique_ptr:独占对象,不允许复制;动态管理分配对象的生命周期
std::unique_ptr<FlyBehavior> _flyBehavior;
std::unique_ptr<QuackBehavior> _quackBehavior;
public:
//explicit表示该构造函数只能显式调用,禁止隐式转换。
/*
* 移动语义:不复制对象,而只是让新的对象名指向原对象名所指对象,并且将原对象所指的置空。
* 完美转发:可以撰写接受任意参数的函数模版,并将其转发到其他函数,目标函数会接受与其转发函数所接受的完全相同的参数。
* &&(右值引用):结合了移动语义和完美转发;可以绑定到临时对象。
* std::move(把实参强制转换为右值):无条件地右值类型的强制类型转换;
* std::forward:仅对绑定到右值的引用实施向右值类型的强制转换。
* 针对右值引用实施std::move,万能引用实施std::forward
*/
explicit Duck(std::unique_ptr<FlyBehavior> &&flyBehavior ={},
std::unique_ptr<QuackBehavior> &&quackBehavior={})
:_flyBehavior(std::move(flyBehavior))
,_quackBehavior(std::move(quackBehavior)){
}
//上下文允许在运行时替换策略对象。
void setFlyBehavior(std::unique_ptr<FlyBehavior>&&flyBehavior){
_flyBehavior = std::move(flyBehavior);
}
void setQuackBehavior(std::unique_ptr<QuackBehavior>&&quackBehavior){
_quackBehavior = std::move(quackBehavior);
}
//执行飞
void performFly() const{
_flyBehavior->fly();
}
//执行叫
void performQuack() const{
_quackBehavior->quack();
}
void swim() const{
std::cout<<"All ducks float, even decoys"<<std::endl;
}
virtual void display() const = 0;
};
3.2.3 实现接口类的飞行为和叫行为
- 实现飞行为:
#include <iostream>
#include "FlyBehavior.h"
//公有继承:基类的公有成员和保护成员的访问属性在派生类中不变
class FlyWithWings: public FlyBehavior{
public:
void fly() const override{
std::cout<<"I'm flying"<<std::endl;
}
};
class FlyNoWay: public FlyBehavior{
public:
void fly() const override{
std::cout<<"I can't fly"<<std::endl;
}
};
class FlyRocketPowered : public FlyBehavior{
public:
void fly() const override{
std::cout<<"I'm fly with rocket!"<<std::endl;
}
};
- 实现叫行为:
#include "QuackBehavior.h"
#include <iostream>
class Quack: public QuackBehavior{
void quack() const override{
std::cout<<"quack"<<std::endl;
}
};
class MuteQuack : public QuackBehavior{
public:
void quack() const override{
std::cout<<"<<Silence>>"<<std::endl;
}
};
class Squeak: public QuackBehavior{
void quack() const override{
std::cout<<"Squack"<<std::endl;
}
};
3.2.4 实现具体的鸭子行为
- 绿头鸭
#include "Duck.h"
#include "Quack.h"
#include "FlyWithWings.h"
#include <iostream>
class MallardDuck : public Duck{
public:
//子类继承父类的构造
//不能直接从初始化列表初始化基类成员,应该使用父类的构造函数
explicit MallardDuck(std::unique_ptr<FlyWithWings>&& flyWithWings ={},
std::unique_ptr<Quack>&& quack={})
: Duck(std::move(flyWithWings), std::move(quack)){
}
//虚函数方法重写
void display() const override{
std::cout<<"I'm a real Mallard duck"<<std::endl;
}
};
- 模型鸭
#include "Duck.h"
#include "FlyNoWay.h"
#include "Quack.h"
class ModelDuck :public Duck{
public:
explicit ModelDuck(std::unique_ptr<FlyNoWay>&& flyNoWay,
std::unique_ptr<Quack>&& quack)
: Duck(std::move(flyNoWay), std::move(quack)){
}
void display() const override{
std::cout<<"I'm a model duck"<<std::endl;
}
};
3.2.5 具体调用
#include "MallardDuck.h"
#include "ModelDuck.h"
#include "FlyRocketPowered.h"
void testCode() {
//make_unique函数:动态分配指定类型的内存,并返回一个指向分配内存的唯一指针
MallardDuck mallardDuck(std::make_unique<FlyWithWings>(),
std::make_unique<Quack>());
mallardDuck.performQuack();
mallardDuck.performFly();
ModelDuck modelDuck(std::make_unique<FlyNoWay>(),
std::make_unique<Quack>());
modelDuck.performFly();
modelDuck.setFlyBehavior(std::make_unique<FlyRocketPowered>());
modelDuck.performFly();
}
int main() {
testCode();
return 0;
}
4 Python实现
4.1 涉及知识点
- 抽象基类(继承ABC):不能被实例化,只能被继承。
- 抽象方法(
@abstractmethod
):继承的子类必须实现的方法。 - 属性(
@property,@flyBehavior.setter
):调用时不需要加(),为的是提供更加友好访问类数据的方式。 - 构造函数(
def __init__
):初始化类成员变量。 - 继承(
super().__init__
):继承父类的属性。 from __future__ import annotations
:允许你在程序中声明变量或函数的类型,并由编译器或其他工具来检查代码的类型是否正确。有助于提高代码的可读性,并且有助于发现类型错误。
4.2 实现代码
4.2.1 实现两个接口飞行为和叫行为
from abc import ABC, abstractmethod
# 抽象基类
class FlyBehavior(ABC):
# 抽象方法
@abstractmethod
def fly(self):
pass
class QuackBehavior(ABC):
@abstractmethod
def quack(self):
pass
4.2.2 实现Duck抽象基类(把行为接口作为类成员)
class Duck(ABC):
def __init__(self, flyBehavior: FlyBehavior, quackBehavior: QuackBehavior) -> None:
self._flyBehavior = flyBehavior
self._quackBehavior = quackBehavior
@abstractmethod
def perform(self) -> None:
pass
def performFly(self) -> None:
return self._flyBehavior.fly()
def performQuack(self) -> None:
return self._quackBehavior.quack()
@property
def flyBehavior(self) -> FlyBehavior:
return self._flyBehavior
@property
def quackBehavior(self) -> QuackBehavior:
return self._quackBehavior
@flyBehavior.setter
def flyBehavior(self, fly: FlyBehavior) -> None:
self._flyBehavior = fly
@quackBehavior.setter
def quackBehavior(self, quack: QuackBehavior) -> None:
self._quackBehavior = quack
def swim(self) -> None:
print("All ducks float, even decoys")
4.2.3 实现接口飞行为和叫行为的具体行为
- 具体实现飞行为接口:
class FlyWithWings(FlyBehavior):
def fly(self) -> None:
print("I'm flying")
class FlyNoWay(FlyBehavior):
def fly(self) -> None:
print("I can't fly")
class FlyRocketPowered(FlyBehavior):
def fly(self) -> None:
print("I'm fly with rocket!")
- 具体实现叫行为接口:
class Quack(QuackBehavior):
def quack(self) -> None:
print("Quack")
class Square(QuackBehavior):
def quack(self) -> None:
print("Squack")
class MuteQuack(QuackBehavior):
def quack(self) -> None:
print("<<Silence>>")
4.2.4 具体实现鸭子
- 实现绿头鸭
class ModelDuck(Duck):
def __init__(self, flyBehavior: FlyNoWay, quackBehavior: Quack) -> None:
super().__init__(flyBehavior, quackBehavior)
# 下面的初始化方法,并不能继承父类的属性
# _flyBehavior = flyBehavior
# _quackBehavior = quackBehavior
def perform(self) -> None:
print("I'm a model duck")
- 实现模型鸭:
class MallardDuck(Duck):
def __int__(self, flyBehavior: FlyWithWings, quackBehavior: Quack) -> None:
super().__init__(flyBehavior, quackBehavior)
def perform(self) -> None:
print("I'm a real Mallard duck")
4.2.5 模型调用
# import sys , os
# base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# sys.path.append(base_dir)
# 没有把当前项目加入到PYTHONPATH环境变量
from Strategy.duck import *
if __name__ == "__main__":
mallardDuck = MallardDuck(FlyWithWings(), Quack())
mallardDuck.performQuack()
mallardDuck.performFly()
modelDuck = ModelDuck(FlyNoWay(), Quack())
modelDuck.performFly()
modelDuck.flyBehavior = FlyRocketPowered()
modelDuck.performFly()
5 完整代码
完整的实现代码,见Gitee仓库。如果对您有用的话,欢迎留下star,谢谢。