HEAD FIRST设计模式(一)————设计模式入门

    最近开始看设计模式,找个一本入门的书,就是这本《HEAD FIRST设计模式》,这本书中的示例是用java写的,本以为没有任何java基础会看着相当费劲,但是这本书只用了很简单的java语言就将明白了,小白也能理解书中的代码。文中内容和图片大多来自于书本还有些个人理解,如有不正确的还请指出。

一、设计一个鸭子

    书中是从一个鸭子开始引入设计模式,这里咱们也从鸭子开始。如何设计一个鸭子,作为一名踩在门槛上还没入门的程序员,如何描述鸭子,或者说如何描述一个对象,我这里的对对象的理解就是,对象是具有某些属性和行为东西。那鸭子具有哪些属性和行为呢,鸭子的属性有颜色,有大小,有体重,鸭子的行为有会飞、会叫等等。这样我们就可以写一个超类(superclass),包括鸭子的属性、行为,然后每个具体的对象实现这些属性,从而变成一个真正的鸭子实例。

像书中这个图,我们设计了会叫会游泳的鸭子。这样我们就完成了对鸭子的设计。然而世间万物都在改变,代码和需求也是一样,所以,我们的代码也要不断改变,既然这样,面向对象的编程不仅要实现一个对象,还要考虑未来对象不断的变化,我们的代码能跟着对象的改变而维护。

    那看看设计鸭子公司如何改变鸭子的,公司说:“我们要改变!我们要创新!我们要做大做强再创辉煌!所以我们的鸭子要会飞!”程序员看了,一脸吃屎的表情,哪这么多需求。但是该改还得改,不就是会飞的鸭子吗,在超类中加个fly行为就OK了。改完之后去展示效果,结果你看到公司的所有鸭子都在飞。包括那个橡皮鸭子,在老板面前飞来飞去,你感觉自己工作不保。得改代码,不能让所有的鸭子都飞,橡皮鸭子不能飞。那就在橡皮鸭的具体实现类中重写父类的fly行为,让他不会飞,小问题。过几天,策划想出来一个诱饵鸭,诱饵鸭不会飞也不会叫,过了几天策划想的东西越来越抽象,什么奥特曼鸭子,会飞还会发激光,什么小米鸭子,会模仿小爱同学说话还能和小米汽车蓝牙连接。这什么鬼?每多一个鸭子行为,就要在超类中添加,然后在子类中覆写这些函数。这累死也搞不完,我们又得改变代码逻辑。

    于是我们把代码结构改成这样

我们把所有鸭子共有的行为剥离出来,放在一个超类中,而特殊的行为都拿出来,放在一边,把那些具有特殊行为的鸭子继承不同的特殊行为。这实际上已经引出了设计模式第一个原则

设计原则一:找出应用中可能需要变化的之处,把它们独立出来,不要和那些不需要变化的代码混在一起。

    然而上面结构依然存在一个问题,那就是,java不能多继承,所以除了Duck这个类,其他的都是接口对象,而java中接口对象不具有实现代码,所以,这和C++有什么关系呢,C++可以多继承,C++也没有接口这个特殊对象,所有对象都一样。今天不是要讨论语言,而是,这种变化量与不变量分离的思想给我们带来了新的思考:不变的部分是对象共有的,通过一个超类实现昂,并由所有对象继承,而变化的部分是未来需要拓展的,我们如何实现拓展呢。回到鸭子的例子,我们应该在鸭子鸭子类中包含设定鸭子行为的方法,这样就可以在“运行时”动态的“改变”的鸭子的行为。这就引出了设计模式第二个原则

设计原则二:针对接口的编程,而不是针对实现编程。

    这个原则还挺抽象,啥事接口,对于java来说,java有interface类和implement类,用于定义接口和实现,可是C++中没有interface和implement关键词,什么是接口,如何用C++做一个接口呢,后面会解释我关于接口的理解以及C++如何实现接口。

    什么是接口?这里所谓的“接口”有多个含义,接口是一个“概念“,也是一种Java的interface构造。你可以在不涉及Java interface的情况 下,“针对接口编程” ,关键就在多态。利用多态,程序可以针对超类型编程,执行时会根据实际状况执行到真正的行为,不会被绑死在超类型的行为上。“针对超 类型编程”这句话,可以更明确地说成‘“变量的声明类型应该是超类型,通常是一个抽象类或者是一个接口,如此,只要是具体实现此超类型的类所产生的对象,都可以指定给这个变量。这也意味着,声明类时不用理会以后执行时的真正对象类型!”(中文翻译的听抽象的,其实英文说的也挺抽象的。我也没太理解想表达的意思),看看书中的例子

// 针对实现的编程
Dog d = new Dog();
d.bark();

// 针对接口的超类型编程
Animal animal = new Dog();
animal.makeSound();

// 运行时指定对象
a = getAnimal();
a.makeSound();

    忽略第三个,看看第二个,针对接口的编程,实际上就是多态,我们指定对象的类型是Animal类型,但是具体对象是Dog类型,在多态中,这样接口会调用接口实现的方法。再看看上面最后一句话:“变量的声明类型应该是超类型,通常是一个抽象类或者是一个接口,如此,只要是具体实现此超类型的类所产生的对象,都可以指定给这个变量。这也意味着,声明类时不用理会以后执行时的真正对象类型!”。如何扩展?就是将变量声明成一个接口,利用多态去具体实现这个接口,当我们扩展这个接口的时候就是在扩展这个变量。

    回到鸭子的实例,鸭子的行为和叫声是变化量,那就用一个接口表示

    这样鸭子总体就变成这样

这样看看java语言是如何实现鸭子的

// Duck.ava文件
public abstract class Duck{
    FlyBehavior flyBehavior;
    QuackBehavior quackBehavior;
    
    public Duck(){}
    public abstract void display();

    public void performFly(){
        flyBehavior.fly();
    }
    
    public void performQuack(){
        quackBehavior.quack();
    }

    public void swim() {
        System.out.println("All ducks float, even decoys!");
    }
}
// FlyBehavior.java
public interface FlyBehavior{
    public void fly();
}

// FlyWithWinngs.java
public class FlyWithWings implements FlyBehavior{
    public void fly(){
        System.out.prinln("I'm flying!");
    }
}

// FlyNoWay.java
public class FlyNoWay implements FlyBehavior{
    public void fly(){
        System.out.prinln("I can't fly!");
    }
}
// QuackBehavior.java
public interface QuackBehavior{
    public void quack();
}


// Quack.java
public class Quack implements QuackBehavior{
    public void quack(){
        System.out.println("Quack");
    }
}


// MuteQuack.java
public class MuteQuack implements QuackBehavior{
    public void quack(){
        System.out.quack("<<Silence>>");
    }
}

        
// Squeak.java
public class Squeak implements QuackBehavior{
    public void quack(){
        System.out.quack("Squeak");
    }
}
// MallardDuck.java
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");
    }
}
//MiniDuckSimulator.java
public class MiniDuckSimulator{
    public static void main(String[] args){
        Duck mallard = new MallardDuck();
        mallard.performQuack();
        mallard.perfermFly();
    }
}

没有java,也没有jvm虚拟机,没跑过上面代码,按书上抄的,不知道复制下来能不能跑。最终鸭子的结构如下

    当你将两个类结合起来使用,就像上图一样,这是组合(composition),这种做法和“继承”不同的地方在于,鸭子的行为不是继承来的,而是和适当的行为对象“组合”来的。这也使第三个设计原则

设计原则三:多用组合,少用继承。

二、C++代码

QuackBehavior.h

// QuackBehavior.h
#pragma once
#include <iostream>
class QuackBehavior {
public:
	virtual void quack() {
		std::cout << "default quack" << std::endl;
	}
	virtual ~QuackBehavior() {}
};

FlyBehavior.h

// FlyBehavior.h
#pragma once
#include <iostream>
class FlyBehavior {
public:
	virtual void fly() {
		std::cout << "default fly" << std::endl;
	}
	virtual ~FlyBehavior() {}
};

FlyImplements.h

// FlyImplements.h
#pragma once
# include "FlyBehavior.h"
class FlyWithWngs : public FlyBehavior {
public:
	void fly() final;
	virtual ~FlyWithWngs() {}
};

class FlyNoWay : public FlyBehavior {
public:
	void fly() final;
	virtual ~FlyNoWay() {}
};

FlyImplements.cpp

#include <iostream>
#include "FlyImplements.h"

using std::cout;
using std::endl;

void FlyWithWngs::fly() {
	cout << "I'm flying!" << endl;
}

void FlyNoWay::fly() {
	cout << "I can't fly" << endl;
}

QuackImplements.h

// QuackImplements.h
#pragma once
#include <iostream>
#include "Quackbehavior.h"

class Quack : public QuackBehavior {
public:
	void quack() final;
	virtual ~Quack() {}
};


class MuteQuack : public QuackBehavior {
	void quack() final;
	virtual ~MuteQuack() {}
};


class Squeak : public QuackBehavior {
	void quack() final;
	virtual ~Squeak() {}
};

QuackImplements.cpp

// QuackImplements.cpp
#include <iostream>
#include "QuackImplements.h"

using std::cout;
using std::endl;

void Quack::quack() {
	cout << "Quack" << endl;
}


void MuteQuack::quack() {
	cout << "<<Slience>>" << endl;
}


void Squeak::quack() {
	cout << "Squeak" << endl;
}

Duck.h

// Duck.h
#pragma once
#include "FlyBehavior.h"
#include "Quackbehavior.h"

class Duck {
public:
	Duck() {}
	void display();
	void perfromFly();
	void performQuack();
	void swim();
	void setFlyBehavior(FlyBehavior* fb);
	void setQuackBehavior(QuackBehavior* gb);
	~Duck();

private:
	FlyBehavior* fly;
	QuackBehavior* quack;

};

Duck.cpp

//Duck.cpp
#include <iostream>
#include "Duck.h"

using std::cout;
using std::endl;

void Duck::display() {
	cout << "call function display" << endl;
}


void Duck::perfromFly() {
	if (fly) {
		fly->fly();
	}
}


void Duck::performQuack() {
	if (quack) {
		quack->quack();
	}
}


void Duck::swim() {
	cout << "All ducks float, even decoys!" << endl;
}


void Duck::setFlyBehavior(FlyBehavior* fb) {
	if (fly != nullptr) {
		delete fly;
	}
	fly = fb;
}


void Duck::setQuackBehavior(QuackBehavior* gb) {
	if (quack != nullptr) {
		delete quack;
	}
	quack = gb;
}


Duck::~Duck() {
	if (quack) {
		delete quack;
	}

	if (fly) {
		delete fly;
	}
}

main.cpp

#include "QuackImplements.h"
#include "FlyImplements.h"
#include "Duck.h"


int main() {
	Duck* MallardDuck = new Duck();
	MallardDuck->setFlyBehavior(new FlyWithWngs());
	MallardDuck->setQuackBehavior(new Quack());

	MallardDuck->perfromFly();
	MallardDuck->performQuack();

	MallardDuck->setFlyBehavior(new FlyNoWay());
	MallardDuck->setQuackBehavior(new MuteQuack());

	MallardDuck->perfromFly();
	MallardDuck->performQuack();

	MallardDuck->setQuackBehavior(new Squeak());

	MallardDuck->perfromFly();
	MallardDuck->performQuack();

	delete MallardDuck;

	return 0;
}

输出结果

I'm flying!
Quack
I can't fly
<<Slience>>
I can't fly
Squeak

最后,C++需要自己管理内存,推荐使用智能指针管理内存,避免内存泄漏,上面有点偷懒直接使用了裸指针,注意别忘了释放内存。

  • 31
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值