第1章 UML基础:类的关系

前前后后犹豫好久,到底是写不写设计模式,毕竟自己不是科班出身,最后还是决定根据大神博客和设计模式之禅写下几种常见的

计模式,等到后面使用的时候再来复习。不管是跟着什么资料在学习,记住一定要思考,一定要敲代码,一定要升华!!!

1、类的关系

1.1、继承(is-a)和实现:继承表示父子关系(子类是特殊的父类),实现有点像接口继承。

   

1.2、依赖(use-a):表示一个类要使用(use)另一个类。

(1)、类图

   表示C21要使用C22类型

(2)、三种依赖方式:函数参数或返回值、局部变量和静态成员函数或变量

class C21
{
public:

    //1、使用函数形参和返回值发生依赖关系
    C22 test(C22 theC22);

    //2、使用局部变量发生依赖关系
    void test()
    {
        C22 theC22; //或C22* theC22 = new C22;
                    //离开这个作用域后the22要销毁
    }

    //3、全局变量或静态变量(函数)发生依赖关系
    void test()
    {
        C22 theC22 = g_C22; //g_C22为全局变量
        C22::func();        //使用类的静态成员函数
    }
};

1.3、关联:是一种平等的、朋友关系。

(1)、双向关联:双方都知道对方的存在,可以使用对方的公有成员变量和函数。

      理解:资源在外部,C31类型 表示的是指向资源的指针。

 a、代码表现:双方拥有对方的一个指针或引用。     

 b、之所以是指针有原因。如果是值(对象)那么就不是关联。因为是值的话,C31对象消失C32对象也会跟着消失。(站在内存的角

度理解,资源分配在哪里)。 组合:整体与部分的关系,而且整体消息部分也会消息,部分不能独立于整体。

(2)、单向关联

   注意依赖和单向关联

a、表示相识关系,指C33知道C34,可以调用C34的公共成员变量和函数

b、代码上表示为C33有C34的指针,而C34对C33一无所知。

(3)、自身关联:自己的内部有一个指向自身的指针或引用

             

1.4、聚合与组合

(1) 聚合:(has-a),表示整体-部分的关系,但部分可以脱离整体而单独存在。

         关联是平等的朋友关系。  聚合:整体和部分的关系

a、如C41聚合C42,但是C42可以离开C41而独立存在。在创建C41类的对象时,一般不会马上创建C42对象,而是等待一个外界的

对象传给它。 资源在外部。

b、当用C++代码来描绘关联和聚合时,都是一个类包含了另外一个类的指针。但是他们是有区别的,这个区别不是C++语法上的差

别,而是语义上的差别聚合是整体和部分的关系,而且关联是平等的朋友关系,比如。张三和李四,是关联。而张三和张三的杯

是聚合。张三和张三的鼻子是组合。

(2)、组合(contains-a):表示整体部分关系,但是部分不能脱离整体单独存在。  如:手脚是身体的一部分,轮子与汽车。

   

a、组合用的是值对象(外部传入的,生命周期与整体一样)

b、聚合是指针。但有时组合也可以用指针,在构造函数中创建对象,析构函数中销毁对象

  区别:聚合,一般其对象指针由类外传入的,而组合是在类内部的构造函数中new出来的。

c、从语义上看,组合与聚合也是不一样的。当表示聚合时,部分可以脱离整体。而组合不行。

2、依赖和聚合/组合、关联的区别

(1)、聚合与组合

a、聚合与组合都是一种整体和部分的关系。(脱离能不能存在)

b、部件的生命周期不同

  聚合关系中,整件不会拥有部件的生命周期,所以整件删除时,部件不会被删除。再者,多个整件可以共享同一个部件。

  组合关系中,整件拥有部件的生命周期,所以整件删除时,部件一定会跟着删除。而且,多个整件不可以同时间共享同一个部件。

c、聚合关系是“has-a”关系,组合关系是“contains-a”关系。

(2)关联和聚合

a、表现在代码层面,和关联关系是一致的,只能从语义级别来区分。

b、关联和聚合的区别主要在语义上,关联的两个对象之间一般是平等的,例如你是我的朋友,聚合则一般不是平等的。

c、关联是一种结构化的关系,指一种对象和另一种对象有联系。

d、关联和聚合是视问题域而定的,例如在关心汽车的领域里,轮胎是一定要组合在汽车类中的,因为它离开了汽车就没有意义

了。但是在卖轮胎的店铺业务里,就算轮胎离开了汽车,它也是有意义的,这就可以用聚合了。

(3)、关联和依赖

a、关联关系中,体现的是两个类、或者类与接口之间语义级别的一种强依赖关系,比如我和我的朋友;这种关系比依赖更强、不

存在依赖关系的偶然性、关系也不是临时性的,一般是长期性的,而且双方的关系一般是平等的。

b、依赖关系中,可以简单的理解,就是一个类A使用到了另一个类B,而这种使用关系是具有偶然性的、临时性的、非常弱的,但

是B类的变化会影响到A。

(4)、注意:

上述的几种关系(依赖、关联、聚合/组合)在代码中可能以指针、引用、值等的方式在另一个类中出现,不拘于形式,只有配合

义,结合上下文来判断。而只给出一段代码让我们来判断是什么关系,还是无法准确判断的。

(5)、所谓的这些关系只是在某个问题域才有效,离开了这个问题域,可能这些关系就不成立了。

这几种关系都是语义级别的,所以从代码层面并不能完全区分各种关系。

总的来说关系的强弱程度:组合>聚合>关联>依赖

3、类关系实例分析

(1)、类图

  

(2)、代码实现

//Cgprs.h和Cgprs.c  

#ifndef __CGPSReceiver_H__
#define __CGPSReceiver_H__

class Cgprs
{
public:
	void navigate(void);
};

#endif

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

using namespace std;

void Cgprs::navigate(void)
{
	cout << "开始使用GPRS导航..." << endl;
}
//CEngine.c和CEngine.h

#ifndef __CENGINE_H__
#define __CENGINE_H__

class CEngine
{
private:
	int mCapacity;
	int mPower;
public:
	CEngine(int capacity, int power);
	~CEngine();
	void start();
	void stop();

	int getCapacity() const; //不能修改成员变量的值
	void setCapacity(int capacity);
	int getPower() const;
	void setPower(int power);
};

#endif

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

using namespace std;
//尽量使用初始化列表成员变量初始化
CEngine::CEngine(int capacity, int power) : mCapacity(capacity), mPower(power)
{

}
CEngine::~CEngine()
{

}
void CEngine::start()
{
	cout << mCapacity << "cc,";
	cout << mPower << "马力的发动机发动了!\n" << endl;
}
void CEngine::stop()
{
	cout << "发动机关闭了!\n" << endl;
}

int CEngine::getCapacity() const
{
	return mCapacity;
}
void CEngine::setCapacity(int capacity)
{
	mCapacity = capacity;
}
int CEngine::getPower() const
{
	return mPower;
}
void CEngine::setPower(int power)
{
	mPower = power;
}

//CWheel.c和h文件

#ifndef __CWHEEL_H__
#define __CWHEEL_H__
#include <string>

using namespace std;

class CWheel
{
private:
	int mNo; //数量
	int mSize; //尺寸
	string mTypeName; //类型
	void check(); //出场检查
public:
	CWheel();
	CWheel(int no, int size, string TypeName); //构造函数

	CWheel(const CWheel& cw); //拷贝构造函数
	CWheel& operator= (const CWheel& cw); //赋值构造函数
};

#endif

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

using namespace std;

CWheel::CWheel()
{

}

CWheel::CWheel(int no, int size, string TypeName) : mNo(no), mSize(size), mTypeName(TypeName)
{
	check();
}

CWheel::CWheel(const CWheel& cw)
{
	mNo = cw.mNo;
	mTypeName = cw.mTypeName;
	mSize = cw.mSize;
}
CWheel& CWheel::operator= (const CWheel& cw)
{
	if (this == &cw)  return *this;

	mNo = cw.mNo;
	mTypeName = cw.mTypeName;
	mSize = cw.mSize;

	return *this;
}

void CWheel::check()
{
	cout << "检查第" << mNo + 1 << "个车轮:型号(" << mTypeName << "),";
	cout << "大小(" << mSize << ")" << endl;
}
//CVehicle.c和.h

#ifndef __CVEHICLE_H__
#define __CVEHICLE_H__
#include <string>
#include "CWheel.h"

using namespace std;
//交通工具类可以实现为接口,抽象类
class CVehicle
{
protected:
	string mColor;
	string mMake;
	int mTopSpeed;
	CWheel mWheel; //车轮与CVehicle是组合关系,声明为值对象
public:
	CVehicle();
	void speedup();
	void slowdown();
	void start();
	void stop();
};

#endif

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

using namespace std;

CVehicle::CVehicle()
{
	
}
void CVehicle::speedup()
{
	cout << "正在加速..." << endl;
}
void CVehicle::slowdown()
{
	cout << "正在减速..." << endl;
}
void CVehicle::start()
{
	cout << "车子开始启动..." << endl;
}
void CVehicle::stop()
{
	cout << "车子停下..." << endl;
}
//CCar.c和.h文件

#ifndef __CCAR_H__
#define __CCAR_H__

#include "CEngine.h"
#include "Cgprs.h"
#include "CVehicle.h"

class CCar : public CVehicle
{
protected:
	CEngine mEngine;      //发动机与CCar类是组合关系,声明为值关系
	Cgprs* mGPSReceiver;  //导航与CCar是聚合关系,声明为指针,由外部传入
public:
	CCar(string color, string make, int speed, Cgprs* gps);
	~CCar();
	void drive();
};

#endif

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

using namespace std;

CCar::CCar(string color, string make, int speed, Cgprs* gps) : mEngine(0, 0)
{
	mColor = color;
	mMake = make;
	mTopSpeed = speed;

	mEngine.setCapacity(mTopSpeed + 1000);
	mEngine.setPower(mTopSpeed - 70);

	cout << mColor << mMake << "车,最高时速:" << mTopSpeed << endl;
	
	mGPSReceiver = gps;
	mWheel = CWheel(1, 36, "A型汽车车轮");
}
CCar::~CCar()
{

}
void CCar::drive()
{
	if (mGPSReceiver)
	{
		mGPSReceiver->navigate();
	}

	mEngine.start();
	speedup();
	cout << "汽车行驶中..." << endl;
	slowdown();

	mEngine.stop();
	stop();
}

//CBicycle.h和.c

#ifndef __CBICYCLE_H__
#define __CBICYCLE_H__

#include "CVehicle.h"

using namespace std;

class CBicycle : public CVehicle
{
public:
	CBicycle();
	~CBicycle();

	void ride();
};

#endif

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

using namespace std;

CBicycle::CBicycle() : CVehicle()
{
	mColor = "白色";
	mMake = "永久";
	mTopSpeed = 20;

	cout << mColor << mMake << "自行车,最高时速:" << mTopSpeed << endl;

	mWheel = CWheel(1, 21, "B型自行车车轮"); //赋值构造函数  赋值式  调用构造函数临时对象
	//mWheel(CWheel(1, 21, "B型自行车车轮")); //拷贝构造  定义式
}
CBicycle::~CBicycle()
{

}

void CBicycle::ride()
{
	start();
	speedup();
	cout << "自行车行驶中..." << endl;
	slowdown();
	stop();
}
//CPerson.c和.h
#ifndef _CPERSON_H_
#define _CPERSON_H_

#include "Cgprs.h"
#include "CBicycle.h"
#include "CCar.h"

class CPerson
{
public:
	Cgprs *mGPSReceiver;            //CGPSReceiver与CPerosn是关联关系(平等、朋友关系),由类外传入

	void drive(CCar* car);          //CCar与CPerson通过形参发生依赖关系
	void ride(CBicycle* bicle);     //CBicycle与CPerson通过形参发生依赖关系
	void use(Cgprs* gps);           //将gps传给mGPSReceiver
};

#endif 

#include "CPerson.h"

void CPerson::drive(CCar* car)
{
	car->drive();
}

void CPerson::ride(CBicycle* bicycle)
{
	bicycle->ride();
}

void CPerson::use(Cgprs* gps)
{
	mGPSReceiver = gps;

	mGPSReceiver->navigate();
}
//main.c

#include <iostream>
#include "CPerson.h"
using namespace std;

int main(void)
{
	CPerson person;

	//GPS
	Cgprs gps;
	//cout << (void*)(&gps) << endl;
	//开车
	CCar* car = new CCar("黑色", "红旗", 200, &gps);
	person.drive(car);
	delete car;

	printf("\n");
	
	//骑自行车
	CBicycle *bicycle = new CBicycle();
	person.ride(bicycle);
	delete bicycle;

	printf("\n");

	//测试GPS
	person.use(&gps);
	
	cin.get();
	return 0;
}

程序测试结果:



写程序的过程中注意几点:

1、注意根据类图定义类的顺序

2、阶段性编译原则


总结:继承  实现(抽象类、接口定义)  依赖(use-a)  关联:语义上是平等的朋友关系

    聚合(has-a):整体-部分关系,部分可以脱离整体存在    组合(contain-a):整体-部分关系,部分不能脱离整体。

特定的类之间的关系,在代码层面上可能相同,但是可以在语义上进行区分。所以类之间的关系,需要在特定的环境下进行区分类之间的关系。

通过上面的例子可以更好的了解类之间的关系,有助于进一步理清后面思路。     

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值