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

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

[复制链接]
gaosmile 发布时间:2020-7-9 12:50
前言
羊哥之前写一篇有趣的文章《答应我,别再if/else走天下了可以吗 | CodeSheep》,在文中使用 Java 语言实现了枚举类、工厂模式和策略模式的三种方式,来消除连环的if/else。内容层层递进,由浅入深的方式我非常喜欢。
看到有留言中有小伙伴想看 C++ 版本的,特此写下了此文(已经过羊哥的同意)。不过由于 C++ 没有枚举类,所以本文不涉及此方式,但本文会带大家一步一步的优化工厂模式策略模式
正文糟糕 if / else 连环
if/else可以说是我们学习编程时,第一个学习的分支语句,简单易理解,生活中也处处有的if/else例子:
老婆给当程序员的老公打电话:“下班顺路买一斤包子带回来,如果看到卖西瓜的,买一个。”  
% }: `) u0 `. l8 [* W6 V& \当晚,程序员老公手捧一个包子进了家门。。。
) H* L0 r  r3 g0 E8 L& z老婆怒道:“你怎么就买了一个包子?!”
5 f; a0 s& H9 }2 ^  V老公答曰:“因为看到了卖西瓜的。”
老婆的思维:
买一斤包子;
/ L* P1 K2 m% S  D8 mif( 看到卖西瓜的 )
! J0 ^0 n1 M+ `" H$ \  U  买一只( 西瓜 );
5 c& q; [( a2 p) E; O
而程序员老公的程序:
if( ! 看见卖西瓜的 ) 8 C. t" R4 j: m. T! e
   买一斤包子;
6 x3 a$ l/ s, ]1 W; h' Jelse
2 e/ y" P4 X. N3 E   买一只( 包子 );

7 f: r4 |! u2 \
非常生生动动的生活例子!如果身为程序员的你,犯了同样的思维错误,别继续问你媳妇为什么,问就是跪键盘
微信图片_20200709124825.png
进入本文正题。考虑以下栗子:一般来说我们正常的后台管理系统都有所谓的角色的概念,不同管理员权限不一样,能够行使的操作也不一样。
  • 系统管理员(ROLE_ROOT_ADMIN):有A操作权限
  • 订单管理员(ROLE_ORDER_ADMIN):有B操作权限
  • 普通用户(ROLE_NORMAL):有C操作权限
    0 V4 B9 J; U4 F: D  m  {/ B5 m) k
假设一个用户进来,我们需要根据不同用户的角色来判断其有哪些行为。使用过多if/else连环写法的我们,肯定下意识就觉得,这不简单嘛,我上演一套连环的写法:
class JudgeRole
; D  A0 n, E5 K" e{) \: j9 r: }8 n6 U' k& }; [) U
public:
( Q7 O. U: ^' T    std::string Judge( std::string roleName )
& U* c4 ~8 x& ]% _/ C  I- @    {+ o+ E$ G. Y- N: _
        std::string result = "";& K; e+ z. d" M: G9 w/ a
        if( roleName == "ROLE_ROOT_ADMIN" )       // 系统管理员# `) _& J. ^5 Q" J+ o9 Q
        {* S; N: q( @+ T( }" z
            result = roleName + "has A permission";
$ E: K6 N6 H0 S7 R, I; E  v        }/ R# _- a5 O$ B) l4 P
        else if( roleName == "ROLE_ORDER_ADMIN" ) // 订单管理员
1 y. E  _9 W! x/ F        {
, k6 E" q. q5 p4 ~8 x* {% }. K            result = roleName + "has B permission";
) p! x5 L1 N* G2 L, l        }
/ y* i6 N, q. C9 f( X        else if( roleName == "ROLE_NORMAL" )       // 普通用户
4 i. k9 ~5 p9 U- Y3 ~" v% R* Q8 N. V        {
. ?  S, k2 F) Y  W. k            result = roleName + "has C permission";2 W( k1 j; |- N9 F
        }0 P7 [* _$ q1 o# S. `) d# K
        return result;# d8 U! L8 c8 l7 k8 J
    }3 ?1 `9 t' R: k1 R7 L
};
& R3 f! ^7 N: k3 G  A( Y  ]
当系统里有几十个角色,那岂不是几十个if/else嵌套,这个视觉效果绝对酸爽……这种实现方式非常的不优雅。
别人看了这种代码肯定大声喊:“我X,哪个水货写的!”
这时你听到,千万不要说:“那我改成switch/case”。千万别说,千万别说哦,否则可能拎包回家了……
微信图片_20200709124828.jpg
因为switch/case和if/else毛区别都没,都是写费劲、难阅读、不易扩展的代码
接下来简单讲几种改进方式,别再 if / else 走天下了。

工厂模式 —— 它不香吗?
不同的角色做不同的事情,很明显就提供了使用工厂模式的契机,我们只需要将不同情况单独定义好,并聚合到工厂里面即可。
首先,定义一个公用接口RoleOperation,类里有一个纯虚函数Op,供派生类(子类)具体实现:
// 基类
8 o2 D) @' w3 }, tclass RoleOperation
& ]" W' `8 ^- i% ?6 S2 ?  K{
/ H. N0 t0 i5 t# npublic:
+ a( b! G0 q0 ~- ?    virtual std::string Op() = 0; // 纯虚函数
! n# |# s  N2 s, `3 ~' J5 @    virtual ~RoleOperation() {} // 虚析构函数, E$ N$ X( [3 g: u. w! y3 I3 n2 C
};

5 S0 B1 l3 _- d8 v4 X
接下来针对不同的角色类,继承基类,并实现 Op 函数:
// 系统管理员(有 A 操作权限)- l* V8 F2 W" g9 U: t& K
class RootAdminRole : public RoleOperation {
9 v1 g3 u/ V: k2 c+ D) r$ I4 b+ ~, {3 Zpublic:0 I9 u5 I  ?3 i) E8 I' P+ U9 B, `3 W. z6 e
    RootAdminRole(const std::string &roleName)
' p9 c3 X7 _' I' V: b, c5 f            : m_RoleName(roleName) {}+ ~. Q' }0 u8 L& Q. G& E

1 ?4 u2 [/ m, V    std::string Op() {5 \2 [+ U" B; j$ x5 s# n+ p
        return m_RoleName + " has A permission";# B* w, E- J- ]9 N$ o
    }
7 v7 K, [4 R7 a# ]( F9 c
/ P$ M  i& W9 Rprivate:: z/ Q5 J7 ^* ]6 ^2 w$ ?
    std::string m_RoleName;
: Y6 X6 F9 Z3 p+ }& E9 N};+ F2 F9 n' P5 ]' Q9 _' B

7 w. v7 V+ @  `4 n" j: a# w- c- I. S' A% U* h7 m
// 订单管理员(有 B 操作权限)* ]8 R% H: k) |- T$ U
class OrderAdminRole : public RoleOperation {
+ {" g4 y# }8 N% g% mpublic:
" ^! ~% z- F3 E3 o) O0 ^    OrderAdminRole(const std::string &roleName), b" [) C& t: ]' R
            : m_RoleName(roleName) {}8 D  e: \  R1 O  ~$ h+ F4 ]; @& K

) A  h! C! F: @  D  Z    std::string Op() {2 d1 ~2 K5 Q' Y
        return m_RoleName + " has B permission";$ s' E0 o; e: O1 Q* P0 l' E9 L/ K
    }
3 @- z6 n& G" _3 `0 e# }7 P; d* A+ G0 }+ F
private:+ |0 O1 |* U# E5 A
    std::string m_RoleName;
! l2 Q- N; _" F( L, J2 g};8 u. T& g$ ^/ O& N! G
4 D6 B, J3 @0 [$ \3 n3 I4 |
// 普通用户(有 C 操作权限)% n, V9 \  [5 ^+ o0 v) p& \3 q
class NormalRole : public RoleOperation {4 [& E6 s. \  l! O% {1 h* {$ L* U2 c/ Y
public:; x  u2 ~' J+ c! z
    NormalRole(const std::string &roleName), [6 G8 l9 {& x) f0 F: k; e
            : m_RoleName(roleName) {}
" Z: I5 F; D  S2 A. t5 Y
4 A4 \0 o% C4 U: N- ~  M) N    std::string Op() {
, ]8 t8 R1 Z, ~        return m_RoleName + " has C permission";/ Z7 [& X2 O/ x9 |& B: S
    }( Z* v' ^7 H' Y+ k1 N+ b0 n

" y( E8 t. N0 b( C9 Dprivate:
: T6 m1 h# {0 O& P9 p    std::string m_RoleName;
. b0 s& r, Y9 I( z};
" O) C( m( n% C# q
接下来在写一个工厂类RoleFactory,提供两个接口:
  • 用以注册角色指针对象到工厂的RegisterRole成员函数;
  • 用以获取对应角色指针对象的GetRole成员函数。

    ) a% f+ n" p3 t- a1 X# M4 T: }* F

2 _6 Q+ ^9 P& s# Y7 _8 N
// 角色工厂+ g: Q/ _/ S% A. s& l9 [
class RoleFactory {
) w/ I, ~  d; L( M- [) Ppublic:5 A% r6 ^) R4 T7 j. x' {
    // 获取工厂单例,工厂的实例是唯一的7 `! A; y2 l. x: a2 l) @
    static RoleFactory& Instance() {6 V9 d. k& E6 ^
        static RoleFactory instance; // C++11 以上线程安全
& ?& l) K- S+ V& n        return instance;' P9 N* j& U& i! p/ N, _+ U
    }) n+ n7 P! F6 T% l) y& h  |/ z

4 ^' Z9 g/ F2 [& Z    // 把指针对象注册到工厂4 Y! s8 \( J# |( n% i0 q
    void RegisterRole(const std::string& name, RoleOperation* registrar) {
5 G, ~2 t' Y! e        m_RoleRegistry[name] = registrar;
1 A% e' o, \* n% s    }
- {  @3 X1 c9 {+ Z, Z7 f7 g% R0 W& s8 Y6 y
    // 根据名字name,获取对应的角色指针对象- \# T4 X+ A; W1 J5 ?7 x5 F" y* a
    RoleOperation* GetRole(const std::string& name) {/ k  @" j$ E7 z8 f* z4 [

1 Q$ A7 ^0 n, Q& {; h( J3 Z8 f        std::map<std::string, RoleOperation*>::iterator it;$ Q1 q$ [# b, V' s& ?

5 A) ^% x/ T6 K  ?  F! A        // 从map找到已经注册过的角色,并返回角色指针对象6 n* S6 J2 Q8 C
        it = m_RoleRegistry.find(name);
6 e6 ^6 q, W# m        if (it != m_RoleRegistry.end()) {: ]  M. o( w8 v- {' F/ w
            return it->second;
! ?7 z) t8 I* D# e$ T: h' K        }
  u" ^9 X* H6 i/ U% c/ J- M" z& k2 C7 q4 s
        return nullptr; // 未注册该角色,则返回空指针/ ^; H' R* j( s3 Z
    }5 o; h$ W3 j4 e. q" D8 Z
* `4 Z  w% E1 ]3 |3 H
private:9 [0 W9 e5 y- @; [% ]8 f3 C
    // 禁止外部构造和虚构) H9 Q: r0 |1 Q9 p
    RoleFactory() {}
) j+ o4 t. d) h+ X    ~RoleFactory() {}
4 }- \3 O# ?6 X* c2 J
: z/ m& S' `- x5 B    // 禁止外部拷贝和赋值操作' T. q: ^) m. K/ i; N
    RoleFactory(const RoleFactory &);
5 h$ G# d# s9 H9 x3 a    const RoleFactory &operator=(const RoleFactory &);" B' C- V, h, z( _- R/ w+ k

6 N% H0 f/ \. @3 F# t    // 保存注册过的角色,key:角色名称 , value:角色指针对象1 i& e, v# O  N3 A* r* I
    std::map<std::string, RoleOperation *> m_RoleRegistry;9 f  k2 [9 J* R: ^
};
( h- N3 r# `3 @# w  F" f, D+ p
把所有的角色注册(聚合)到工厂里,并封装成角色初始化函InitializeRole:
void InitializeRole() // 初始化角色到工厂
+ Z( Y. N% w! r3 U. y- M{3 g5 W( C& q. n2 E- H5 J6 E
    static bool bInitialized = false;6 {# k- l0 Z5 a0 y$ X$ `

3 I6 S  P$ U: n- ~1 v3 E/ C/ V* h9 P    if (bInitialized == false) {( G2 o, R; N, i6 f7 M
        // 注册系统管理员; D+ p4 x( {  `
        RoleFactory::Instance().RegisterRole("ROLE_ROOT_ADMIN", new RootAdminRole("ROLE_ROOT_ADMIN"));$ A5 p: W% Y/ e0 T
        // 注册订单管理员
$ ^4 z; q' ^: P" t        RoleFactory::Instance().RegisterRole("ROLE_ORDER_ADMIN", new OrderAdminRole("ROLE_ORDER_ADMIN"));. g# n3 U& a' j2 c0 @
        // 注册普通用户
1 @+ f0 k! N6 V; p9 u3 u: N        RoleFactory::Instance().RegisterRole("ROLE_NORMAL", new NormalRole("ROLE_NORMAL"));6 t, \  Q4 H4 H6 m; ]. L. z
        bInitialized = true;1 ]( a3 s/ o! f! [
    }
/ q( {  ]; @$ r) F" Y9 s}
1 D& C4 M% `7 D1 U  C
接下来借助上面这个工厂,业务代码调用只需要一行代码,if/else被消除的明明白白:
class JudgeRole {
. ?7 n8 V7 {' u% Rpublic:( x, t! H; [/ x9 ^$ p" B5 y7 X9 c
    std::string Judge(const std::string &roleName) {8 Z  v/ H& @& m$ i
        return RoleFactory::Instance().GetRole(roleName)->Op();
* C" q& ^/ r8 @% s0 y( @5 E6 |; V    }
' i" e% H* N8 C1 d+ _# m& r};

2 V( }9 G" h! L* x
需要注意:在使用Judge时,要先调用初始化所有角色 InitializeRole 函数(可以放在main函数开头等):
int main() {! V9 }% I6 S* ?0 N# [8 L, m, U
    InitializeRole(); // 优先初始化所有角色到工厂* Z, S" h& Z; W8 E2 s0 U! @

- ^% r2 C8 J8 I6 Y    JudgeRole judgeRole;6 y7 I! k9 o4 N7 q& U

9 W4 k! B" d# [    std::cout << judgeRole.Judge("ROLE_ROOT_ADMIN") << std::endl;  v6 i6 v5 Y4 x
    std::cout << judgeRole.Judge("ROLE_ORDER_ADMIN") << std::endl;
( f3 R8 A& W1 \/ Q/ D) A    std::cout << judgeRole.Judge("ROLE_NORMAL") << std::endl;
2 _& G$ v  X8 Q; P, ~2 H3 U. B) B}
$ \3 O0 R* C% u5 ~4 I, [, b
通过工厂模式实现的方式,想扩展条件也很容易,只需要增加新代码,而不需要改动以前的业务代码,非常符合「开闭原则」。
微信图片_20200709124831.jpg
不知道小伙伴发现了没有,上面实现工厂类,虽然看来去井然有序,但是当使用不当时会招致程序奔溃,那么是什么情况会发生呢?
我们先来分析上面的工厂类对外的两个接口:
  • RegisterRole注册角色指针对象到工厂
  • GetRole从工厂获取角色指针对象

    ) N2 Q- W  Z7 s4 v3 g" f
难道是指针对象没有释放导致资源泄露?不,不是这个问题,我们也不必手动去释放指针,因为上面的工厂是「单例模式」,它的生命周期是从第一次初始化后到程序结束,那么程序结束后,操作系统自然就会回收工厂类里的所有指针对象资源。
但是当我们手动去释放从工厂获取的角色指针对象,那么就会有问题了:
RoleOperation* pRoleOperation =  RoleFactory::Instance().GetRole(roleName);; Q7 k- G, k; l. N& }
...* g- b6 v+ \9 S
delete pRoleOperation; // 手动去释放指针对象

/ \' ~* a# y8 R; B& Z( y0 g& Y* O
如果我们手动释放了指针对象,也就导致工厂里 map 中存放的指针对象指向了,当下次再次使用时,就会招致程序奔溃!如下面的例子:
class JudgeRole {+ R* D4 e7 W  m: y. d0 Z7 T; G0 z
public:
# O& D, v* K1 |/ a; g1 _3 r" E    std::string Judge(const std::string &roleName) {
# c- o6 I; }, Y* w        RoleOperation *pRoleOperation = RoleFactory::Instance().GetRole(roleName);
2 k* j# [/ E8 n- c        std::string ret = pRoleOperation->Op();. ~, `& ]+ x& q
        delete pRoleOperation; // 手动去释放指针对象
  F( Z# d. _. |. ]6 K        return ret;
  B5 X1 o$ r+ W: N7 ^) O2 ~% l  n; o    }/ z" B) \+ y. v# r* d
};& b6 }/ V( S% Q$ g

# n( ?5 D: J, C( c- z' yint main() {/ ~3 H( D1 V4 }% @* A3 R2 `8 K. s
    InitializeRole(); // 优先初始化所有角色到工厂
5 o* p5 y8 [# {7 ]( c6 ~
, R: p+ `4 e1 j+ b    JudgeRole judgeRole;3 C1 K9 B, F8 G  M0 M  }: P

- |) n# `( t9 n) Y# J    std::cout << judgeRole.Judge("ROLE_ROOT_ADMIN") << std::endl;/ p, E( i0 O: r" x7 m  ]9 i" u" u1 w
    std::cout << judgeRole.Judge("ROLE_ROOT_ADMIN") << std::endl; // 错误!程序会奔溃退出!1 |6 c' P% ]  m

, r+ j; E% ^& \/ o6 o3 W    return 0;
9 w  @$ K( j- k: i}

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

    9 a3 B& ]- l, [( D# [: Z$ @
我们使用新的注册(聚合)方式,对InitializeRole初始化角色函数改进下,参见下面:
void InitializeRole() // 初始化角色到工厂
5 s5 ]4 B7 K, E5 V  R* a{
5 a3 O$ Q' f1 T# U2 G3 Z, l    static bool bInitialized = false;
: f, I6 S2 q5 ]% {' `6 U
# C, a5 F& ^& d6 b1 Z) U    if (bInitialized == false) {
+ X( N  X. W7 N+ |2 L) w$ A        // 注册系统管理员: S$ @3 n* H% ~
        static ProductRegistrar<RoleOperation, RootAdminRole> rootRegistrar("ROLE_ROOT_ADMIN");3 M. u6 t& b0 w5 v( ~
        // 注册订单管理员
, \' J* m* D5 O: Y& l4 _        static ProductRegistrar<RoleOperation, OrderAdminRole> orderRegistrar("ROLE_ORDER_ADMIN");* f7 j0 E% V" e3 v
        // 注册普通用户5 V8 S  J5 t% ?- b3 O
        static ProductRegistrar<RoleOperation, NormalRole> normalRegistrar("ROLE_NORMAL");
+ L  x9 K4 C" i1 o& r6 [, A        bInitialized = true;
' a3 }, H7 l. p    }
4 h$ O  d6 A/ |( C% v}

* i4 M" P" H+ U. P# k4 c2 E
2. 从工厂获取角色指针对象的函数是GetProduct,需注意的是:
  • 使用完角色指针对象后,需手动delete资源。
    9 X" B5 }1 F# H( @6 Q1 c& Y9 _4 O7 S
我们使用新的获取角色对象的方式,对Judge函数改进下,参见下面:
class JudgeRole {( t- W, _: M, R( C! R/ J1 L! X2 y; l
public:
! y& b) y  O8 b$ Y    std::string Judge(const std::string &roleName) {# d' i/ S' j# P7 v; a1 g2 M
        ProductFactory<RoleOperation>& factory = ProductFactory<RoleOperation>::Instance();
  O) d3 z8 d! O" p$ u" v        // 从工厂获取对应的指针对象- x( ]3 j; e' y/ N' `3 t/ m) ]
        RoleOperation *pRoleOperation = factory.GetProduct(roleName);
6 D4 u+ ]3 l& L: I' o        // 调用角色的对应操作权限
% q% s$ _, \7 C* |, c        std::string result = pRoleOperation->Op();) M, ~; \  S8 K+ h; S
        // 手动释放资源
' l7 p/ h% D8 W  ?. }. _        delete pRoleOperation;: P3 ?2 q+ p4 `/ u6 k9 D
        return result;; K3 G! u' V8 A% C3 R$ l
    }% w9 }8 Q8 X& B( g! n
};
/ v7 U6 b, n+ a+ U2 H; B: B
唔,每次都手动释放资源这种事情,会很容易遗漏。如果我们遗漏了,就会招致了内存泄漏。为了避免此概率事情的发生,我们用上「智能指针],让它帮我们管理吧:
class JudgeRole {1 T- I2 w/ w5 K8 M
public:$ _$ Q$ w' \1 E! N$ ?
    std::string Judge(const std::string &roleName) {
+ d; l: {9 I/ V# ~        ProductFactory<RoleOperation>& factory = ProductFactory<RoleOperation>::Instance();/ a" v% G. m- A5 Y
        std::shared_ptr<RoleOperation> pRoleOperation(factory.GetProduct(roleName));
3 g* i2 P6 Q5 h( |+ |8 \( ]        return pRoleOperation->Op();
+ F$ x2 K& {" d) Q    }
+ z* F) T0 \( y+ g: W};
; h$ ?# x0 X% r6 g
采用了std::shared_ptr引用计数智能指针,我们不在需要时刻记住要手动释放资源的事情啦(我们通常都会忘记……),该智能指针会在当引用次数为 0 时,自动会释放掉指针资源
来,我们接着来,除了工厂模式,策略模式也不妨试一试

策略模式 —— 它不香吗?
策略模式和工厂模式写起来其实区别也不大!策略模式也采用了面向对象的继承和多态机制
微信图片_20200709124838.gif
在上面工厂模式代码的基础上,按照策略模式的指导思想,我们也来创建一个所谓的策略上下文类,这里命名为RoleContext:
( ~& a' v; g% Y
class RoleContext {
) m" _5 v0 a; v0 d* X# hpublic:
# [! @: f; g8 {9 `& R    RoleContext(RoleOperation *operation) : m_pOperation(operation) {
8 D" Q) I9 ~1 @% R* K7 l: j    }
& D5 ~/ ~1 E% E0 D% V' Q! p( C# I$ W4 u$ Q; ?3 V$ m% g
    ~RoleContext() {; t1 v& A+ i+ d- {2 p' n  e3 G3 i; G1 O5 A
        if (m_pOperation) {
) g5 ~0 q- f( f- L; x2 \3 @            delete m_pOperation;0 Q% p& ~" R4 m0 P6 l* k
        }3 O* N! o, [. S6 z4 u
    }
6 }. b% V2 o1 G+ }: E3 a6 y/ C! j
    std::string execute() {. L' u6 I$ l5 g) G! O) s5 G$ W1 X
        return m_pOperation->Op();
0 u0 `+ R; n6 c0 a% q    }* y0 n$ |! Y) I" k/ z/ k. f. t

2 C. S2 o' u6 K# z; `7 F4 Z% vprivate:
: z1 C# E7 U+ u, J( Z9 L5 ]    // 禁止外部拷贝和赋值操作/ {# c. V) ?! p9 x( M: P
    RoleContext(const RoleContext &);
8 G0 d" [: u8 O; K7 D4 l/ f1 F    const RoleContext &operator=(const RoleContext &);+ G. }+ K3 M6 @3 X) }$ d) i* ~9 {
8 Y, r5 t6 F! S
    RoleOperation *m_pOperation;
3 d1 ]* B1 }# q; U% q};
! v) e1 y# L/ {1 B) P
很明显上面传入的参数operation就是表示不同的「策略」。我们在业务代码里传入不同的角色,即可得到不同的操作结果:
class JudgeRole {
  a" U: D0 E$ K( M1 Jpublic:. ]3 X' T# l: l* z
    std::string Judge(RoleOperation *pOperation) {6 p) Y/ ~( @* ^( u- Q  f# [; Z3 i
        RoleContext roleContext(pOperation);" c, D% F* Z8 f
        return roleContext.execute();5 ~7 x1 ^4 f* W) h0 ~
    }5 ^/ n4 J  p- r# Y# Z
};
$ Q1 P3 E& `5 H( Y
$ j* U- t4 {  G/ R1 dint main() {
) M6 |2 n6 \, X- e2 |2 C    JudgeRole judgeRole;; q0 g) B* m& J& F: P8 s8 T
+ w$ {6 Q* J/ o- K! \
    std::cout << judgeRole.Judge(new RootAdminRole("ROLE_ROOT_ADMIN")) << std::endl;9 Q: N* ~  K2 j5 Q
    std::cout << judgeRole.Judge(new OrderAdminRole("ROLE_ORDER_ADMIN")) << std::endl;
) e; L0 t( y9 O5 L2 t! U    std::cout << judgeRole.Judge(new NormalRole("ROLE_NORMAL")) << std::endl;- B+ d: P$ {, C
# m7 l+ J  R( w+ u( I
    return 0;3 h8 w' u: e  ]/ Y2 P" o
}

8 G* b- p$ H: U0 p4 H" U7 D" E
当然,上面策略类还可以进一步优化:
  • 用模板技术进一步封装,使其不限制于角色类。

    7 H* s9 ?  `7 S: n/ g8 _
// 策略类模板
( k9 K0 y, r8 X! s; z( m3 L8 x& X// 模板参数 ProductType_t,表示的是基类; j2 d  v/ [$ H2 w
template <class ProductType_t>' t9 V$ c' r5 k3 ~6 `
class ProductContext {
# R, i7 S* D1 P" U; @public:
+ z, C4 E0 r: A. L8 H8 {    ProductContext(ProductType_t *operation) ' F0 M. N& B) a2 I) ?
                : m_pOperation(operation) {! B# }  \' X/ D8 f. X0 M. [: J
    }; W0 E& P* j6 O# S
" m! v1 n+ W, V, Q/ S* F, Y
    ~ProductContext() {+ I2 L! X4 {+ @+ y! n6 Y
        if (m_pOperation) {7 i' ]! P/ t7 [0 l$ [# |1 y
            delete m_pOperation;
7 ?. ?( j8 f# f0 h6 \2 Z# @" Z+ M0 f        }0 e" m: A: F4 Z' H1 I' |4 U  y
    }6 p' W2 \5 f1 [7 c

4 ?, M5 V- q* K' L* c1 {    std::string execute() {
  P$ i; M6 X- n: L2 U- d        return m_pOperation->Op();1 {) W( |5 \; V+ Y0 _; I$ t
    }
  M4 E, J) M( a: y' b/ D! y% }/ [% L' Z: ^, U4 @1 j2 _3 m
private:
& D, ?! [1 N" A) b  t" m  j, S    // 禁止外部拷贝和赋值操作# d( o; ]: r* t% f# Z7 e
    ProductContext(const ProductContext &);% Q6 d% r: e  _5 I: Y  K2 w
    const ProductContext &operator=(const ProductContext &);
! H9 i5 j5 s& w' C, o$ v7 c) g! t9 j" f, o0 h- O& N- s
    ProductType_t* m_pOperation;
+ ]; T+ ?/ s. d2 U9 \9 y! J8 a};
% S# d' S7 S- V5 R9 Y
使用方式,没太大差别,只需要指定类模板参数是基类(如本例 RoleOperation) 即可:
class JudgeRole {
1 ?# d7 e6 m' M/ W3 |9 G  a. R8 kpublic:
8 j' u# x5 U  _! w8 f$ o$ ^* E* T8 F    std::string Judge(RoleOperation *pOperation) {
) L1 ?( x# F7 t, h: ^+ g        ProductContext<RoleOperation> roleContext(pOperation);
$ Z( D8 Z" l" A6 ]        return roleContext.execute();
6 t7 E9 |1 {1 p& A, u' x9 v7 i    }
2 x# V. Y* I4 m5 ^" l: V};

( d* F! i/ f. K; P5 M
共勉
C++ 和 Java 语言都是面向对象编程的方式,所以都是可以通过面向对象和多态特性降低代码的耦合性,同时也可使得代码易扩展。所以对于写代码事情,不要着急下手,先思考是否有更简单、更好的方式去实现。
C++ 之父 Bjarne Stroustrup 曾经提及过程序员的三大美德是懒惰、急躁、傲慢,其中之一的懒惰这个品质,就是告知我们要花大力气去思考,避免消耗过多的精力个体力(如敲代码)。

: X8 g7 X! F7 q6 X
收藏 评论0 发布时间:2020-7-9 12:50

举报

0个回答

所属标签

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