当前位置:必发365电子游戏 > 编程 > 可在 Duck 中加普通虚函数,的派生类世襲 fly 的叁个缺省完成
可在 Duck 中加普通虚函数,的派生类世襲 fly 的叁个缺省完成
2019-12-19

1  会飞的野鸭 

  Duck 基类,含成员函数 Swim(卡塔尔(قطر‎ 和 Display(卡塔尔;派生类 MallardDuck,RedheadDuck 和 RubberDuck,各自重写 Display(卡塔尔国

class Duck 
{
public:
    void Swim();
    virtual void Display();
};

class MallardDuck : public Duck 
{
public:
    void Display(); // adding virtual is OK but not necessary
};

class RedheadDuck ...

class RubberDuck ...

  现在必要,为赤麻鸭扩充飞的技术 -- Fly,应该怎么样陈设吧?

C++ 之 计谋情势,计策情势

1  会飞的野鸭 

  Duck 基类,包涵四个成员函数 (swim, display卡塔尔(قطر‎;派生类 MallardDuck,RedheadDuck 和 RubberDuck,各自重写继承自基类的 display 成员函数

class Duck {
public:
    void swim();
    virtual void display();
};

class MallardDuck : public Duck {
public:
    void display(); // adding virtual is OK but not necessary
};

class RedheadDuck ...

  将来需要,为钻水鸭增添会飞的技能 -- fly,那么应该怎么样设计呢?

1.1  继承

  思量到不要全数的野鸭都会飞,可在 Duck 中加个普通虚函数 fly,则“会飞”的派生类世袭 fly 的贰个缺省实现,而“不会飞”的派生类重写 fly 的贯彻

void Duck::fly() {  std::cout << "I am flying !" << std::endl;  }

void RubberDuck::fly() {  std::cout << "I cannot fly !" << std::endl;  }

必发365电子游戏,1.2  接口

  实际上,使用相近虚函数来兑现多态并不是良策,在前文 C++11 之 override 关键字中的 “1.2 日常虚函数” 已经颇负解释,常用的代表方法是 “纯虚函数 + 缺省贯彻”,

就要 fly 在基类中扬言为纯虚函数,同期写叁个缺省实现

  因为是纯虚函数,所以唯有“接口”会被一连,而缺省的“达成”却不会被接续,是还是不是调用基类里 fly 的缺省贯彻,则决定于派生类里重写的 fly 函数

void MallardDuck::fly() { Duck::fly(); } 

void RedheadDuck::fly() { Duck::fly(); }

1.3  设计格局

  到如今截至,并未应用设计格局,但难题看起来已经被解决了,实际上选取或不采用设计格局,决定于实际供给,也在于开拓者

  <Design 帕特terns> 中,关于战术情势的适用情景,如下所示:

1) many related classes differ only in their behavior

2) you need different variants of an algorithm

3) an algorithm uses data that clients shouldn't know about

4) a class defines many behaviors, and these appear as multiple conditional statements in its operations

  分明,赤麻鸭的逐生机勃勃派生类归属 “related classes”,关键就在于“飞”那一个作为,假设只是将“飞”的一举一动,简单划分为“会飞”和“不会飞”,则不选取设计方式完全能够

  如果“飞行方式”,随着派生类的增添,起码会有几十种;恐怕视“飞行方法”为风度翩翩种算法,以后还恐怕会不断改正;再或“飞行方法”作为包装算法,提必要第三方应用。

那就是说那时候,设计形式的市场股票总值就反映出来了 -- 易复用,易扩充,易维护。

  而第 4) 种适用情景,多见于重构之中 -- "Replace Type Code with State/Strategy"

 

2  设计条件

  在引出战术方式早前,先来看面向对象的八个布置规范

1)  隔开分离变化identify what varies and separate them from what stays the same

   Duck 基类中, 很分明“飞行情势“是变化的,于是把 fly 择出来,和剩余不改变的相间开来

2)  编制程序到接口program to an interface, not an implementation

  分出 fly 之后,将其卷入为贰个接口,里面完毕各样差别的“飞行方法” (风姿罗曼蒂克层层”算法“卡塔尔(英语:State of Qatar),增加或改造算法都在此个接口里面举行。“接口”对应于 C++ 就是说梅止渴基类,

将在“飞市场价格势”封装为 FlyBehavior 类,该类中证明 fly 成员函数为纯虚函数

class FlyBehavior {
public:
    virtual void fly() = 0;
};

class FlyWithWings : public FlyBehavior {
public:
    virtual void fly();
};

class FlyNoWay ...class FlyWithRocket ...

  具体落到实处各个不相同的算法 -- “飞行方法”,如下所示:

void FlyWithWings::fly() {  std::cout << "I am flying !" << std::endl;  }

void FlyNoWay::fly() {  std::cout << "I cannot fly !" << std::endl;  }

void FlyWithRocket::fly() {  std::cout << "I am flying with a rocket !" << std::endl; }

3)  复合 > 继承:favor composition (has-a) over inheritance (is-a)

   <Effective C++> 条款 32 中提到,国有世襲便是“is-a”,而条款 38 则提及 Composition (复合或结成卡塔尔国 的贰个意义是 “has-a”。因此,可以在 Duck 基类中,

注脚 FlyBehavior 类型的指针,如此,只需通过指针 _pfB 便可调用相应的”算法“ -- ”飞行方式“

class Duck {
public:
    ...
private:
    FlyBehavior* _pfB;  // 或 std::shared_ptr<FlyBehavior> _pfB;
};

 

3  攻略形式

3.1  内容

  就算不懂设计格局,唯有从严依照地方的八个安顿标准,则最终的安插性思路也会和攻略形式相似,恐怕只是有的细微处的反差

  下边来看政策格局的具体内容和布局图:

  Defines a family of algorithms,  encapsulates each one,  and makes them interchangeable.  Strategy lets the algorithm vary independently

from clients that use it.

  必发365电子游戏 1

  Context 指向 Strategy (由指针达成卡塔尔(قطر‎;Context 通过 Strategy 接口,调用生机勃勃连串算法;ConcreteStrategy 则实现了生机勃勃多种切实的算法

3.2  智能指针

  上例中,战术情势的“接口” 对应于抽象基类 FlyBehavior,“算法达成”分别对应派生类 FlyWithWings, FlyNoWay, FlyWith罗克et,“援引”对应 _pfB 指针

  为了简化内部存款和储蓄器管理,能够将 _pfB 申明为一个“智能指针”,同不时间在 Duck 类的布局函数中,先导化该“智能指针”

Duck::Duck(std::shared_ptr<FlyBehavior> pflyBehavior) : _pfB(pflyBehavior) {}

  直观上看, Duck 对应于 Context,但 Duck 基类并不直接通过 FlyBehavior 接口来调用各类“飞行方法” -- 即“算法”,实际是其派生类 MallardDuck,RedheadDuck 和RubberDuck,那样,就必要在每一种派生类的结构函数中,起头化 _pfB

MallardDuck::MallardDuck(std::shared_ptr<FlyBehavior> pflyBehavior) : Duck(pflyBehavior) {}

  然后,在 Duck 基类中,通过指针 _pfB, 达成了对 fly 的调用

void Duck::performFly()
{
    _pfB->fly();
}

  除了在布局函数中初阶化 _pfB 外,还可在 Duck 类中,定义叁个setFlyBehavior 成员函数,动态的安装“飞行方法”

void Duck::setFlyBehavior(std::shared_ptr<FlyBehavior> pflyBehavior)
{
    _pfB = pflyBehavior;
}

  最后,main 函数如下:

void main()
{
    shared_ptr<FlyBehavior> pfWings = make_shared<FlyWithWings>();
    shared_ptr<FlyBehavior> pfRocket = make_shared<FlyWithRocket>();

    // fly with wings
    shared_ptr<Duck> pDuck = make_shared<MallardDuck>(pfWings);
    pDuck->performFly();

    // fly with a rocket
    pDuck->setFlyBehavior(pfRocket);
    pDuck->performFly();
}

 

小结:

1卡塔尔(英语:State of Qatar)  面向对象的多个安顿标准:隔断变化,编制程序到接口,复合 > 世袭

2卡塔尔  战术情势首要涉及的是“意气风发多种算法“,熟识其适用的三种现象

 

参照他事他说加以考察资料:

 <大话设计格局> 第二章

 <Head First Design Patterns> chapter 1

 <Effective C++> item 32, item 38

 <Design Paterns> Strategy

 <Refactoring> chapter 8

之 计谋格局,计策情势 1 会飞的红鸭 Duck 基类,富含多个分子函数 (swim, display卡塔尔(英语:State of Qatar);派生类 MallardDuck,RedheadDuck 和 RubberDuck,各自重写继承...

1.1  继承

  思忖到不要全体的野鸭都会飞,可在 Duck 中加普通虚函数 Fly(卡塔尔(قطر‎,则“会飞”的接轨 Fly(卡塔尔(英语:State of Qatar) ,“不会飞”的重写 Fly(卡塔尔国

void Duck::Fly() {  std::cout << "I am flying !" << std::endl;  }

void RubberDuck::Fly() {  std::cout << "I cannot fly !" << std::endl;  }

1.2  接口

  用平时虚函数并不是良策,C++11 之 override 关键字 “1.2 普通虚函数” 中已经表明。替代方法是 “纯虚函数 + 缺省兑现”,就要基类中的 Fly(卡塔尔评释为纯虚函数,同期写一个缺省完成

  因为是纯虚函数,所以独有“接口”会被持续,而缺省的“实现”却不会被一而再一而再再而三,是或不是调用 Fly(卡塔尔 的缺省兑现,则决计于重写的 Fly(卡塔尔(英语:State of Qatar)

void MallardDuck::Fly() { Duck::Fly(); } 

void RedheadDuck::Fly() { Duck::Fly(); }

1.3  设计方式

  到近些日子结束,并未设计情势,但难点风度翩翩度化解了。实际上用不用设计方式,决意于实际必要,也决计于开采者。

  <Design Patterns> 中,关于计谋格局的适用情景,如下所示:

1) many related classes differ only in their behavior

2) you need different variants of an algorithm

3) an algorithm uses data that clients shouldn't know about

4) a class defines many behaviors, and these appear as multiple conditional statements in its operations

  显著,海番鸭的次第派生类归于 “related classes”。关键就在于“飞”那一个作为,若是只是将“飞”的展现,轻易划分为“会飞”和“不会飞”,则毫不设计格局完全能够。

  假设“飞行方法”,随着派生类的加码,最少会有几十种;或然视“飞行方式”为生龙活虎种算法,今后还可能会不断修正;再或“飞行方式”作为包装算法,提供给第三方选取那正是说此时,设计方式的市场股票总值就体现出来了 -- 易复用,易扩张,易维护。

  而第 4卡塔尔(قطر‎种适用情景,多见于重构之中,代替一些尺度采用语句 -- "Replace Type Code with State/Strategy"

 

2  设计标准

  在引出计谋格局此前,先看面向对象的多个规划基准

1)  隔离变化identify what varies and separate them from what stays the same

   Duck 基类中, “飞行格局“是调换的,于是把 Fly(卡塔尔择出来,和剩余不改变的相间开来

2)  编制程序到接口program to an interface, not an implementation

  分离Fly(卡塔尔国,将其包装为八个接口,里面达成各类差别的“飞行方法” (蓬蓬勃勃雨后玉兰片”算法“卡塔尔(قطر‎,增添或涂改算法都在那个接口里实行。

  “接口”对应于 C++ 正是空泛基类,故可将“飞行情势”封装为 FlyBehavior 类,并在类中扬言 Fly(卡塔尔(英语:State of Qatar) 为纯虚函数

class FlyBehavior 
{
public:
    virtual void Fly() = 0;
};

class FlyWithWings : public FlyBehavior 
{
public:
    virtual void Fly();
};

class FlyNoWay ...

class FlyWithRocket ...

  具体达成种种差异的算法 -- “飞行方法”,如下:

void FlyWithWings::Fly() {  std::cout << "I am flying !" << std::endl;  }

void FlyNoWay::Fly() {  std::cout << "I cannot fly !" << std::endl;  }

void FlyWithRocket::Fly() {  std::cout << "I am flying with a rocket !" << std::endl; }

3)  复合 > 继承:favor composition (has-a) over inheritance (is-a)

   国有世袭正是 “is-a”,而 Composition (复合或结成卡塔尔(英语:State of Qatar) 的含义是 “has-a”,故此,可在 Duck 基类中,申明 FlyBehavior 型指针,如此,只需通过指针 _pfB 便可调用相应的”算法“ -- ”飞行方法“

class Duck 
{
    ...
private:
    FlyBehavior* fb_;  // 或 std::unique_ptr<FlyBehavior> fb_;
};

 

3  攻略形式

3.1  内容

  纵然不懂设计方式,只要严厉依照固守 隔断变化 --> 编制程序到接口 --> 复合 四个原则,则规划思路也会和方针情势相通:

  上边是战略方式的具体内容:

  Defines a family of algorithms,  encapsulates each one,  and makes them interchangeable.  Strategy lets the algorithm vary independently from clients that use it.

  必发365电子游戏 2

  Context 指向 Strategy (由指针实现卡塔尔国;Context 通过 Strategy 接口,调用一应有尽有算法;ConcreteStrategy 实现了一文山会海现实的算法

3.2  智能指针

  上例中,战术方式的“接口” 对应于 FlyBehavior 类,“算法完毕”分别对应派生类 FlyWithWings, FlyNoWay, FlyWith罗克et,“援引”对应 fb_ 指针

  为了简化内部存款和储蓄器管理,可将 fb_ 注脚为壹个“智能指针”,如此,则无需手动达成析构函数,接收编写翻译器暗中认可生成的就能够。

Duck::Duck(FlyBehavior *fb)
    : fb_(fb)
{}

3.3  分析 

  直观上看, Duck 对应于 Context,实际上是其派生类 MallardDuck 等,通过 FlyBehavior 接口来调用各类“飞行形式”。因而,须求在各种派生类的布局函数中,开端化 fb_

MallardDuck::MallardDuck(FlyBehavior *fb)
    : Duck(fb)
{}

 然后,在 Duck 基类中,通过指针 fb_, 实现对 Fly() 的调用

void Duck::PerformFly()
{
    fb_->Fly();
}

  除了在布局函数中初始化 fb_ 外,还可在 Duck 类中,定义三个SetFlyBehavior 成员函数,动态的安装“飞行方法”

void Duck::SetFlyBehavior(FlyBehavior *fb)
{
    fb_ = fb;
}

3.4  main 函数  

  因为 main 实践达成后,程序也就得了了,所以对于简易程序,new 了指针后,能够毫不 delete

int main ()
{
    FlyBehavior *pfWings = new FlyWithWings;
    FlyBehavior *pfNo = new FlyNoWay;
    FlyBehavior *pfRocket = new FlyWithRocket;

    // fly with wings
    Duck *pDuck = new MallardDuck(pfWings);
    pDuck->PerformFly();

    // fly with a rocket
    pDuck->SetFlyBehavior(pfRocket);
    pDuck->PerformFly();
}

  

 代码链接:

 

小结

1卡塔尔国  面向对象的四个兼顾基准:隔绝变化,编制程序到接口,复合 > 世襲

2卡塔尔  战术方式首要涉嫌的是“意气风发多级算法“,熟稔其适用的多种情景

 

仿效资料

可在 Duck 中加普通虚函数,的派生类世襲 fly 的叁个缺省完成。 <大话设计情势> 第二章

 <Head First Design Patterns> chapter 1

 <Effective C++> item 32, item 38

 <Design Patterns> Strategy

 <Refactoring> chapter 8

  Herb Sutter, GotW #91 Solution: Smart Pointer Parameters

 

上一篇:没有了
下一篇:没有了