你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

C++ 版本——优化工厂模式和策略模式

[复制链接]
gaosmile 发布时间:2020-7-9 12:50
前言
羊哥之前写一篇有趣的文章《答应我,别再if/else走天下了可以吗 | CodeSheep》,在文中使用 Java 语言实现了枚举类、工厂模式和策略模式的三种方式,来消除连环的if/else。内容层层递进,由浅入深的方式我非常喜欢。
看到有留言中有小伙伴想看 C++ 版本的,特此写下了此文(已经过羊哥的同意)。不过由于 C++ 没有枚举类,所以本文不涉及此方式,但本文会带大家一步一步的优化工厂模式策略模式
正文糟糕 if / else 连环
if/else可以说是我们学习编程时,第一个学习的分支语句,简单易理解,生活中也处处有的if/else例子:
老婆给当程序员的老公打电话:“下班顺路买一斤包子带回来,如果看到卖西瓜的,买一个。”  
! J2 q- I  E; G* e( ]+ c9 D) a: R3 P当晚,程序员老公手捧一个包子进了家门。。。
) d- r- e( \2 D* V2 _' x$ u% z老婆怒道:“你怎么就买了一个包子?!”
8 N  `3 w! U, \% C3 Y5 @9 ~9 i老公答曰:“因为看到了卖西瓜的。”
老婆的思维:
买一斤包子;2 Z$ ?, i+ `5 O
if( 看到卖西瓜的 )
7 Q: x8 t  z' _6 {/ @  买一只( 西瓜 );

: d" y6 Z) e" c9 V! x
而程序员老公的程序:
if( ! 看见卖西瓜的 ) & I% N2 c* @8 ]% F& o7 d
   买一斤包子;
7 j. P, q2 K  c# Jelse
; n7 o3 n  e. j; n3 P6 J& q3 Q   买一只( 包子 );

/ }3 J+ h9 B2 ^7 ^: a3 X- \7 q
非常生生动动的生活例子!如果身为程序员的你,犯了同样的思维错误,别继续问你媳妇为什么,问就是跪键盘
微信图片_20200709124825.png
进入本文正题。考虑以下栗子:一般来说我们正常的后台管理系统都有所谓的角色的概念,不同管理员权限不一样,能够行使的操作也不一样。
  • 系统管理员(ROLE_ROOT_ADMIN):有A操作权限
  • 订单管理员(ROLE_ORDER_ADMIN):有B操作权限
  • 普通用户(ROLE_NORMAL):有C操作权限
    * O: B  C& O5 R: F# J
假设一个用户进来,我们需要根据不同用户的角色来判断其有哪些行为。使用过多if/else连环写法的我们,肯定下意识就觉得,这不简单嘛,我上演一套连环的写法:
class JudgeRole6 [3 q1 {4 L$ |" \
{: E& |4 o; k3 D
public:
5 P' h( O1 m; Y0 T    std::string Judge( std::string roleName )) _& A7 g7 y. u$ A# H- g5 R" x
    {
8 x# n/ T; g8 F" K+ m        std::string result = "";
8 D& D5 a+ K' |7 y& B        if( roleName == "ROLE_ROOT_ADMIN" )       // 系统管理员+ }4 |! X, j6 k+ ~4 R
        {
) p, b- o5 a  {. H. I( D, K            result = roleName + "has A permission";* H/ H5 G2 e% N, V8 s
        }
7 A" t# b5 M9 R8 ~% v* O4 n; X6 Y, }        else if( roleName == "ROLE_ORDER_ADMIN" ) // 订单管理员7 k* f" C& |' [  _( C- M% N! ~
        {
/ z4 \6 q) z  k7 m            result = roleName + "has B permission";3 H0 k) T$ {: S" s3 h2 ^* [0 h+ M, `
        }, g3 M# q+ K  {, U2 O' x% H
        else if( roleName == "ROLE_NORMAL" )       // 普通用户" ~: r4 Z/ Z& j( L& `) T
        {
3 @6 }; L2 R2 i3 _1 @            result = roleName + "has C permission";
' A- i% P( y( _0 @# C        }" m  g! z* X+ q  T' m1 J6 w8 f( ?
        return result;
: H7 \; r3 F$ p% X, J3 o    }! W8 S: `9 P( h+ i* t6 E& @
};

( g4 s+ j4 b+ j8 @% C( P6 Y
当系统里有几十个角色,那岂不是几十个if/else嵌套,这个视觉效果绝对酸爽……这种实现方式非常的不优雅。
别人看了这种代码肯定大声喊:“我X,哪个水货写的!”
这时你听到,千万不要说:“那我改成switch/case”。千万别说,千万别说哦,否则可能拎包回家了……
微信图片_20200709124828.jpg
因为switch/case和if/else毛区别都没,都是写费劲、难阅读、不易扩展的代码
接下来简单讲几种改进方式,别再 if / else 走天下了。

工厂模式 —— 它不香吗?
不同的角色做不同的事情,很明显就提供了使用工厂模式的契机,我们只需要将不同情况单独定义好,并聚合到工厂里面即可。
首先,定义一个公用接口RoleOperation,类里有一个纯虚函数Op,供派生类(子类)具体实现:
// 基类7 G  k( W9 K) a2 h: V
class RoleOperation
% K- e+ m5 E3 ?0 R! O6 e8 Z0 X{
8 W4 b! P7 ^! u0 I. s# d- Rpublic:' C% Z; Z, J" H9 T; X% y7 U
    virtual std::string Op() = 0; // 纯虚函数
. [! a4 N, h% B2 P    virtual ~RoleOperation() {} // 虚析构函数
0 q5 @& @6 u8 M% x; M7 X# z" B2 `};
& h' ]4 U# X* D' [" e
接下来针对不同的角色类,继承基类,并实现 Op 函数:
// 系统管理员(有 A 操作权限)  I% Y; y+ C: w1 N
class RootAdminRole : public RoleOperation {2 F, a' ~! n1 j' D# Y. b
public:
3 n" }0 K5 L; J" m. `. g( {5 L( }    RootAdminRole(const std::string &roleName)
' ?, ]: f1 X' a3 n- o            : m_RoleName(roleName) {}  F3 W. o( [% ]. Z$ }: P
) q% C* x% ?' M! R2 `1 }& w
    std::string Op() {2 b5 ^. U) Y$ M" u0 z. d
        return m_RoleName + " has A permission";8 F" z( D0 c% a" N- u4 W
    }6 ^7 h4 Y7 Q. f! s- U4 W- P
3 G/ K4 I8 \2 z0 m, G( b3 H
private:  B9 ^) J& r: E: j$ S! p
    std::string m_RoleName;" p1 `( \! L6 p; d: {3 a/ i& T2 N
};/ s! W9 b8 m' J3 J) b5 {

* k0 T7 K) L3 C; P% w1 C! P- s6 T, ~* I% e
// 订单管理员(有 B 操作权限)
& P, P- [& {) j9 [1 c5 Zclass OrderAdminRole : public RoleOperation {1 n: B1 W% v' l/ }! ?
public:6 ?9 q1 n, \& G: F
    OrderAdminRole(const std::string &roleName)
2 E7 i! l& k* D) j. r- U: X            : m_RoleName(roleName) {}
9 s3 S6 e% ]/ C$ r8 ^: v5 y7 t) L( Q- `, r* p
    std::string Op() {. `6 ~( u" z! F) j( P* _7 t% @
        return m_RoleName + " has B permission";
" q* O+ v5 {, k    }
. k  P  o  z! h% d( w
# G0 {0 O3 l3 [- `0 }* oprivate:
( o2 e2 G/ s+ H6 x& e    std::string m_RoleName;0 S; n! {2 I% `/ l' s- S: N% A6 w0 P
};/ }% n# ]' Z- W( P5 C; y% m5 @  H

4 P1 ]% ^! {4 }& S, R* C// 普通用户(有 C 操作权限)
0 t9 n6 ^; _' _" I, Zclass NormalRole : public RoleOperation {+ F) s/ a2 B! N0 Z- M' @, b
public:- i% S/ y# p2 B8 P2 k) e+ }4 X
    NormalRole(const std::string &roleName)" ]# S3 j! ]7 I; b: E
            : m_RoleName(roleName) {}
0 }3 ]( c5 n# f0 _; J6 \9 q* K" [
    std::string Op() {
* H7 ]/ y! [7 [; ~* f        return m_RoleName + " has C permission";
' H- r: j. |. Y/ v! W( ^    }
( h) x3 \/ j$ @
$ Y8 ?$ }2 D/ U4 aprivate:
4 ^. I; S5 g" a: M7 M    std::string m_RoleName;8 }+ H; h* y3 E4 y
};

9 D: X/ f9 D3 s0 ?# h
接下来在写一个工厂类RoleFactory,提供两个接口:
  • 用以注册角色指针对象到工厂的RegisterRole成员函数;
  • 用以获取对应角色指针对象的GetRole成员函数。

    0 h, {, a0 B' i
. c! N; d, p+ q
// 角色工厂
- p% l' K: j/ k! n3 g# S7 Eclass RoleFactory {/ K8 f, E6 _4 W3 R  U  [
public:
2 }, e7 ~( T' J3 f) d6 c# f7 W    // 获取工厂单例,工厂的实例是唯一的
5 C3 x& ~$ I$ z: [  E) z) W    static RoleFactory& Instance() {) b: z5 |  T( X/ `# F, Y1 M! ?' H
        static RoleFactory instance; // C++11 以上线程安全
7 }9 g$ N* Y( j6 u        return instance;- G3 @8 ?0 w3 S9 B
    }
2 U0 p& J8 N0 R% ?* }% a* I# k7 b! F; Q/ M& ^9 i9 s% b6 \: D1 a
    // 把指针对象注册到工厂
+ r  i  Q3 j9 t    void RegisterRole(const std::string& name, RoleOperation* registrar) {9 j3 ^' a: {. c* U
        m_RoleRegistry[name] = registrar;
  o9 D( O1 J* h    }
3 K* A& ^" F; p8 W- L0 G) m+ Y
- I3 c4 G3 W& }: }) K- k" [' U    // 根据名字name,获取对应的角色指针对象
" z+ F0 Q0 o4 G- x* p! J+ ?    RoleOperation* GetRole(const std::string& name) {
9 K9 |3 ]( X; W- y  R
% n; \! Y- h6 i5 h9 Z        std::map<std::string, RoleOperation*>::iterator it;
8 E" F9 n1 E6 ]& l1 ~/ r6 V/ \( `3 G7 u$ I: Z
        // 从map找到已经注册过的角色,并返回角色指针对象
  `7 x4 V/ T- B* w  r+ R9 Y        it = m_RoleRegistry.find(name);4 ?4 @( y3 }5 b' q
        if (it != m_RoleRegistry.end()) {
) T$ `3 @0 p' b6 r& ^8 z* a            return it->second;
/ F$ _( B& f6 Q0 _, b6 P        }) ^8 Q% m- K* P' E# v' E
: z* h/ t8 Q1 M6 w( a
        return nullptr; // 未注册该角色,则返回空指针
7 |4 I+ G1 R: I7 d2 B/ }9 ]    }
: S/ q* n& d  c. v2 z
% Z7 B. A1 H& cprivate:
$ K' [3 e$ y( M- x7 X- W! X    // 禁止外部构造和虚构
7 P; _' A( C& X2 }* K: O3 p    RoleFactory() {}
$ x- G! V) T+ Q4 E. y9 M; b    ~RoleFactory() {}
' p* P( G7 F) A. W5 J) O
. U. P8 \% c! x! z7 b3 E    // 禁止外部拷贝和赋值操作
! m  `# i8 [; c( M    RoleFactory(const RoleFactory &);& N" H# J: X  z* r/ P
    const RoleFactory &operator=(const RoleFactory &);9 C# u2 t, ~& F) j1 W. b9 i
9 F% }9 @# D; Z& f) z
    // 保存注册过的角色,key:角色名称 , value:角色指针对象1 W. v* I/ _; ?6 b
    std::map<std::string, RoleOperation *> m_RoleRegistry;
5 _8 a; Q+ x9 H8 l3 ?# Z};
# U  b. a, n& M3 y0 t
把所有的角色注册(聚合)到工厂里,并封装成角色初始化函InitializeRole:
void InitializeRole() // 初始化角色到工厂
* w3 {% O% }" h{
, Z  \0 k0 G! H6 O& z    static bool bInitialized = false;" q" t) i0 o" H0 h! b5 {8 w

  D9 d4 w) Z2 ?3 J    if (bInitialized == false) {( y* E) J" D8 l; a% V; n% U
        // 注册系统管理员+ J$ }3 n7 _% Z
        RoleFactory::Instance().RegisterRole("ROLE_ROOT_ADMIN", new RootAdminRole("ROLE_ROOT_ADMIN"));
- J, C" s9 U! w; c. P        // 注册订单管理员
  z4 P+ e+ @. S) p; h        RoleFactory::Instance().RegisterRole("ROLE_ORDER_ADMIN", new OrderAdminRole("ROLE_ORDER_ADMIN"));
3 s1 [7 p: S6 ~; u. x8 U0 `        // 注册普通用户3 `4 T3 h! U& \2 b# X
        RoleFactory::Instance().RegisterRole("ROLE_NORMAL", new NormalRole("ROLE_NORMAL"));9 B" o3 T! Q. k3 S2 V! x
        bInitialized = true;5 h1 K; I/ h3 L1 \
    }/ G6 {3 e5 w8 i$ @; Q
}

- X, l0 _. x0 I- V3 R0 [9 C7 |$ J
接下来借助上面这个工厂,业务代码调用只需要一行代码,if/else被消除的明明白白:
class JudgeRole {* H- U) V7 e# ?$ p6 c2 h
public:
0 j) w8 O# {7 I    std::string Judge(const std::string &roleName) {
! m" P  U/ M; A. E, h, i. n        return RoleFactory::Instance().GetRole(roleName)->Op();7 H# v* N; T, x" n4 j. d+ b0 x
    }1 y6 y: H3 ]% e
};
0 c1 F# `+ r- v6 d  r1 ?$ t& P
需要注意:在使用Judge时,要先调用初始化所有角色 InitializeRole 函数(可以放在main函数开头等):
int main() {* J, V3 f1 `  ^# Y3 T
    InitializeRole(); // 优先初始化所有角色到工厂
  F) r9 t" F* n/ Z% H- [
: n) p! H6 G3 C' @, L. E$ e( u. ?9 z8 T    JudgeRole judgeRole;5 J, {- X- C+ [9 Q$ |- [- o

+ l' K+ H: C3 N) W: x    std::cout << judgeRole.Judge("ROLE_ROOT_ADMIN") << std::endl;
  s$ ]4 \: X; ~7 ]0 U/ Z    std::cout << judgeRole.Judge("ROLE_ORDER_ADMIN") << std::endl;) Q9 [7 p* j# p5 ^5 e# t+ k6 B3 _
    std::cout << judgeRole.Judge("ROLE_NORMAL") << std::endl;/ Q6 p5 o) |: _' p. K
}
2 b2 B$ U9 o3 V0 ?: ~
通过工厂模式实现的方式,想扩展条件也很容易,只需要增加新代码,而不需要改动以前的业务代码,非常符合「开闭原则」。
微信图片_20200709124831.jpg
不知道小伙伴发现了没有,上面实现工厂类,虽然看来去井然有序,但是当使用不当时会招致程序奔溃,那么是什么情况会发生呢?
我们先来分析上面的工厂类对外的两个接口:
  • RegisterRole注册角色指针对象到工厂
  • GetRole从工厂获取角色指针对象
      ]' D0 h9 [1 d- X8 h5 t
难道是指针对象没有释放导致资源泄露?不,不是这个问题,我们也不必手动去释放指针,因为上面的工厂是「单例模式」,它的生命周期是从第一次初始化后到程序结束,那么程序结束后,操作系统自然就会回收工厂类里的所有指针对象资源。
但是当我们手动去释放从工厂获取的角色指针对象,那么就会有问题了:
RoleOperation* pRoleOperation =  RoleFactory::Instance().GetRole(roleName);
2 J! [7 W9 m0 U0 @8 y% x; k+ v. t...
; j# `9 [7 }& R2 v8 |, i( B1 V  Ndelete pRoleOperation; // 手动去释放指针对象
! ]! R0 ~/ I5 {: j# Y4 v) ^, T
如果我们手动释放了指针对象,也就导致工厂里 map 中存放的指针对象指向了,当下次再次使用时,就会招致程序奔溃!如下面的例子:
class JudgeRole {
: s) G9 \' Y5 M4 B: z. mpublic:/ l/ J/ `6 G) S  \+ g
    std::string Judge(const std::string &roleName) {! O' T, A" _. C8 ?  N4 F# D; l; q* _5 l
        RoleOperation *pRoleOperation = RoleFactory::Instance().GetRole(roleName);
: w$ L3 r6 R, Y4 f( V+ R        std::string ret = pRoleOperation->Op();
' V. M' v) m4 U7 K$ A! Z        delete pRoleOperation; // 手动去释放指针对象
# L) g9 Y5 s) V        return ret;3 [0 O) g: s/ _2 j% x
    }
- a: i8 _" H7 N9 K};
# f$ a/ A& \- h3 K- q* n/ p0 \1 L  @+ w9 p
int main() {
2 U$ F/ s$ [& j4 _; p5 M" A* x    InitializeRole(); // 优先初始化所有角色到工厂6 a$ s( C+ C! w: R  j" S

( m7 {9 \  a) e, q+ @0 A- ~    JudgeRole judgeRole;8 `6 E7 D: \  p8 ?- c" O5 [3 j! x4 @/ ^

! m& ]7 n; _) X  O    std::cout << judgeRole.Judge("ROLE_ROOT_ADMIN") << std::endl;' p" J7 D; @7 ?
    std::cout << judgeRole.Judge("ROLE_ROOT_ADMIN") << std::endl; // 错误!程序会奔溃退出!
7 g; l/ [8 H  y4 y( v+ p$ t9 c# g8 ~8 |- m8 H3 m6 T6 B! ^( P/ B
    return 0;
/ Z& l- z, t* B9 t}

3 A4 v/ u  h) ~5 N
上面的代码在使用第二次ROLE_ROOT_ADMIN角色指针对象时,就会招致程序奔溃,因为ROLE_ROOT_ADMIN角色指针对象已经在第一次使用完后,被手动释放指针对象了,此时工厂map存放的就是空指针了。
可否优化呢?因为有的程序员是会手动释放从工厂获取的指针对象的。
上面的工厂类的缺陷就在于,new初始化的指针对象只初始化了一次,如果手动 释放了指针对象,就会导致此指针对象指向空,再次使用就会导致系统奔溃。
为了改进这个问题,那么我们把 new初始化方式放入工厂类获取指针对象的成员函数里,这也就每次调用该成员函数时,都是返回新new初始化过的指针对象,那么这时外部就需要由手动释放指针对象了
下面的工厂类,改进了上面问题,同时采用模板技术,进一步对工厂类进行了封装,使得不管是角色类,还是其他类,只要存在多态特性的类,都可以使用此工厂类,可以说是「万能」的工厂类了:
微信图片_20200709124834.jpg 「万能」工厂
接下来把新的「万能」工厂模板类,使用到本例的角色对象。
1. 把角色注册(聚合)到工厂的方式是构造ProductRegistrar对象 ,使用时需注意:
  • 模板参数ProductType_t指定的是基类(如本例RoleOperation
  • 模板参数ProductImpl_t指定的是派生类(如本例 RootAdminRole、OrderAdminRole 和 NormalRole

    ; x. n+ w+ {+ Z# J% [7 P' U/ O
我们使用新的注册(聚合)方式,对InitializeRole初始化角色函数改进下,参见下面:
void InitializeRole() // 初始化角色到工厂0 y1 n! [- d' ~# j- d# d& ?
{
; H, M- B( x/ Y' b8 Q: e0 Q    static bool bInitialized = false;, d3 M$ p+ x- t

* o( M: s0 @' v. o. s; m% P: w    if (bInitialized == false) {
0 [' i& u0 t9 l; }. N' \/ i- R        // 注册系统管理员" x; z* ~& x, G" M# |
        static ProductRegistrar<RoleOperation, RootAdminRole> rootRegistrar("ROLE_ROOT_ADMIN");
- r; i( i& F3 H9 c2 ~        // 注册订单管理员
, B: Y% \# l; @5 k7 ]# u7 {        static ProductRegistrar<RoleOperation, OrderAdminRole> orderRegistrar("ROLE_ORDER_ADMIN");6 h: m2 ^5 M: _( p6 A' N$ O3 m. z
        // 注册普通用户- d1 r6 g+ z3 k( N: ~
        static ProductRegistrar<RoleOperation, NormalRole> normalRegistrar("ROLE_NORMAL");
& \" ~. V1 J& u  r        bInitialized = true;) s- G7 g* W7 \# @, M
    }2 K7 c  z+ K3 ~, E, r" J
}
# z8 {4 a: ?1 n: c: u' b" C' R' ~
2. 从工厂获取角色指针对象的函数是GetProduct,需注意的是:
  • 使用完角色指针对象后,需手动delete资源。

    4 R5 {$ F# I' d9 u' t7 o& V: S
我们使用新的获取角色对象的方式,对Judge函数改进下,参见下面:
class JudgeRole {; k2 X$ W& z! g1 Z
public:
- j  d2 @5 c8 |    std::string Judge(const std::string &roleName) {
( y& n, C' J* b% t5 k        ProductFactory<RoleOperation>& factory = ProductFactory<RoleOperation>::Instance();
3 T& ]5 F! G0 I  X7 ]; A        // 从工厂获取对应的指针对象7 d/ q7 k" A0 ^9 p6 F
        RoleOperation *pRoleOperation = factory.GetProduct(roleName);
) ~; T4 S6 c/ W/ J        // 调用角色的对应操作权限# d7 e# h/ M% ^5 x
        std::string result = pRoleOperation->Op();
- b. I0 w! ~: o5 q. {. B        // 手动释放资源, D" ?' x% l  S3 l. ?- N! v
        delete pRoleOperation;5 G0 j# `2 B! I% [8 S- y' o9 Z
        return result;( N' O$ q9 U  a: ?4 J- K9 {
    }
+ N6 [+ A% L9 V% \};

0 Q( L1 o- J( \' `- b0 L
唔,每次都手动释放资源这种事情,会很容易遗漏。如果我们遗漏了,就会招致了内存泄漏。为了避免此概率事情的发生,我们用上「智能指针],让它帮我们管理吧:
class JudgeRole {
9 r8 z% o/ v& ]& y% @0 z. ppublic:. E0 e' b3 r, Q. N: O
    std::string Judge(const std::string &roleName) {
- C1 z' N. T, B7 U2 \! W; W        ProductFactory<RoleOperation>& factory = ProductFactory<RoleOperation>::Instance();
1 ^1 D8 j6 P0 z6 @* c% ^3 [& W& ]        std::shared_ptr<RoleOperation> pRoleOperation(factory.GetProduct(roleName));
5 J; D0 b! q5 l- H        return pRoleOperation->Op();
! l9 p3 v0 o- ]. c6 Y- L( ?- o7 A    }
8 A  K7 a* P! J* `* I! O};
' {2 J& v2 v: I/ S+ G
采用了std::shared_ptr引用计数智能指针,我们不在需要时刻记住要手动释放资源的事情啦(我们通常都会忘记……),该智能指针会在当引用次数为 0 时,自动会释放掉指针资源
来,我们接着来,除了工厂模式,策略模式也不妨试一试

策略模式 —— 它不香吗?
策略模式和工厂模式写起来其实区别也不大!策略模式也采用了面向对象的继承和多态机制
微信图片_20200709124838.gif
在上面工厂模式代码的基础上,按照策略模式的指导思想,我们也来创建一个所谓的策略上下文类,这里命名为RoleContext:
; J; _/ q) u1 l+ H8 ^
class RoleContext {# ~! e! t7 R/ U5 h) o6 Y
public:
! {- N) C0 O0 I- L    RoleContext(RoleOperation *operation) : m_pOperation(operation) {) @$ K9 f. {2 q; k) R4 S0 K% D
    }% C8 P) e0 Z# Q0 S
) l1 y6 O8 `- L& u5 \: ]6 `$ F6 a
    ~RoleContext() {
4 J; Y; K2 j, K8 S' i8 q6 E        if (m_pOperation) {0 b+ D1 g+ z, }
            delete m_pOperation;7 @! m& n# q$ D* Q5 C1 ]% b# g
        }, u6 B8 Y: x/ r) C
    }
+ ]2 n  H% ^! \; G& Y) u; {# M3 P2 e" j% j- m# e  C5 M
    std::string execute() {0 G; K  T" s. r2 f
        return m_pOperation->Op();
7 o, \1 J3 R1 \- z4 W    }
# l7 ?5 t9 p% v& f: x4 f" @( M
% c1 w5 \/ F- ]private:; o4 N3 K5 x9 r6 r1 ?; B3 D: _5 k
    // 禁止外部拷贝和赋值操作
! J4 E( z+ R) y    RoleContext(const RoleContext &);
/ f  _/ o, K2 a$ B9 |( ]4 [    const RoleContext &operator=(const RoleContext &);0 ~1 M! g1 V; E/ E# O

( `; j: `8 f0 I+ o4 ]    RoleOperation *m_pOperation;
* L' A1 x  W! N* y4 H: x};

( \! d% ]2 b" Z5 S7 w
很明显上面传入的参数operation就是表示不同的「策略」。我们在业务代码里传入不同的角色,即可得到不同的操作结果:
class JudgeRole {
4 r& n8 H1 p8 n/ opublic:
& f  b4 }1 ?( {% ~0 ~6 ]    std::string Judge(RoleOperation *pOperation) {
7 X/ D. K* A" z! e1 r& Y        RoleContext roleContext(pOperation);
* h6 B! p8 J6 t, |& [9 h: x        return roleContext.execute();
" r# D* p) {! X8 t3 ^    }; v/ L5 f5 x* \) F& j
};: O  A8 n4 G2 C" w" [$ [7 u0 F. n. f
! f# w% g8 C# }9 `9 u
int main() {
( }' f  C$ Q; G( q    JudgeRole judgeRole;
8 |6 X' u; s) X0 o( P4 H# y
  y, K; B7 ?& n+ ^    std::cout << judgeRole.Judge(new RootAdminRole("ROLE_ROOT_ADMIN")) << std::endl;
' r7 i! X! I0 v; b9 ?    std::cout << judgeRole.Judge(new OrderAdminRole("ROLE_ORDER_ADMIN")) << std::endl;( ~8 @/ k8 y; h+ P: \) W
    std::cout << judgeRole.Judge(new NormalRole("ROLE_NORMAL")) << std::endl;! f: V1 I- v. ?
1 C2 D, e- m1 N( M
    return 0;% h# w! u9 Y( m/ H) j
}
; x$ c: h) K- @& E$ R+ P2 j2 B
当然,上面策略类还可以进一步优化:
  • 用模板技术进一步封装,使其不限制于角色类。

    ( z0 Q8 G( @6 _( l
// 策略类模板* f1 v3 }7 n" i! H" A" a9 i
// 模板参数 ProductType_t,表示的是基类
% N/ A0 e% f4 f7 N) n8 S7 i% ttemplate <class ProductType_t>4 G% h5 m0 I( d/ ]$ O8 C
class ProductContext {' S4 v% v, U. I6 o/ R
public:
8 j0 l! D& |- l    ProductContext(ProductType_t *operation)
1 j" E' v5 r; s6 z) u9 N" s                : m_pOperation(operation) {
* }$ C' R! Z! t4 K* R    }
9 p! L- x+ c! Z- o( |8 W4 |
) a1 b" H/ L/ }5 A" L    ~ProductContext() {3 S+ `1 X/ s: F4 M# A7 Q
        if (m_pOperation) {- ~. H, }& o5 r7 z
            delete m_pOperation;* }& x1 G6 y; u# \# t
        }
6 i+ z4 M$ x. w: C1 c    }
$ l8 b% b$ |: l" K" A- t
. i8 w# V! R; s2 u0 w    std::string execute() {! Q2 x  y$ ~4 E/ B% c6 p
        return m_pOperation->Op();' J5 \1 i) b- ?" K' |. e
    }! ]3 S4 p$ L: ?3 h+ h7 s

7 Q! ^3 D- D# s$ G& F8 A2 J; r/ Yprivate:
2 Z# W  @+ h  I  J9 S: j    // 禁止外部拷贝和赋值操作
9 f; p0 Y- ^) ]    ProductContext(const ProductContext &);; P& s4 `2 D- j+ f' U7 E& ?
    const ProductContext &operator=(const ProductContext &);
. {6 {) G" z! J( \8 X7 g+ T. W# }- L) W* k  K7 {: y
    ProductType_t* m_pOperation;
3 V1 T& Y7 U0 [9 d4 _};

) N4 F1 L# z6 C
使用方式,没太大差别,只需要指定类模板参数是基类(如本例 RoleOperation) 即可:
class JudgeRole {
4 s$ m* x/ P' P' G( ^8 xpublic:
. y* s) d$ p8 L, u& N+ O& u    std::string Judge(RoleOperation *pOperation) {
+ I8 y, W5 Z' A4 s. I        ProductContext<RoleOperation> roleContext(pOperation);
2 R! Y. l+ d0 T0 ]2 Q        return roleContext.execute();
. O8 k/ n) d, u; l2 I    }8 Y/ _# ]" O* E# Y- O) w
};

+ C1 R7 j8 n: n% j
共勉
C++ 和 Java 语言都是面向对象编程的方式,所以都是可以通过面向对象和多态特性降低代码的耦合性,同时也可使得代码易扩展。所以对于写代码事情,不要着急下手,先思考是否有更简单、更好的方式去实现。
C++ 之父 Bjarne Stroustrup 曾经提及过程序员的三大美德是懒惰、急躁、傲慢,其中之一的懒惰这个品质,就是告知我们要花大力气去思考,避免消耗过多的精力个体力(如敲代码)。
/ ]' i: V0 j0 N
收藏 评论0 发布时间:2020-7-9 12:50

举报

0个回答

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版