1.1什么是设计模式
每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心。这样,你就能一次又一次地使用该方案而不必做重复劳动。
一个模式有四个基本要素:
- 模式名称(pattern name) 一个助记名,它用一两个词来描述模式的问题、解决方案和效果。
- 问题(problem) 描述了应该在何时使用模式。
- 解决方案(solution) 描述了设计的组成成分,它们之间的相互关系及各自的职责和协作方式。
- 效果(consequences) 描述了模式应用的效果及使用模式应权衡的问题。
1.6 设计模式怎样解决设计问题
1.6.1 寻找合适的对象
面向对象程序由对象组成, 对象 包括数据和对数据进行操作的过程,过程通常称为方法或操作。对象在收到客户的请求(或消息)后,执行相应的操作。
面向对象设计方法学的设计方法:
- 挑出名词和动词,进而创建响应的类和操作
- 关注于系统的协作和职责模式
- 对现实世界建模,再将分析时发现的对象转换至设计中。
例题1
一头母牛从出生后,每两年可以生下一头母牛,即在第二年和第四年分别可产下一头母牛,出生后第五年将会死去。假设农场现有一头母牛,N年后农场的母牛数目是多少,编写程序实现
分析:选择牛为对象,年龄是属性,过一年是方法,该方法确定今年牛应该生牛还是死亡。
class CCow
{
public:
CCow()
{
m_iAge = 0;
}
CCow* PassYear()
{
m_iAge++;
if (m_iAge == 2 || m_iAge == 4)
{
return new CCow;
}
if (m_iAge == 5)
{
return NULL;
}
return this;
}
private:
int m_iAge;
};
int Call(int iYear)
{
CPtrList list;
list.AddTail(new CCow);
for (int i = 1; i <= iYear; i++)
{
int iCount = list.GetCount();
POSITION pos = list.GetHeadPosition();
for (int i = 0; i < iCount; i++)
{
CCow* pCur = (CCow*)list.GetNext(pos);
CCow* pRet = pCur->PassYear();
if (pRet == NULL)
{
delete list.RemoveHead();
}
else if (pCur != pRet)
{
list.AddTail(pRet);
}
}
}
int iCount = list.GetCount();
while (!list.IsEmpty())
{
delete list.RemoveHead();
}
return iCount;
}
其它实现方法:
size_t func(int years)
{
size_t tab[5] = {1};
size_t i, total = 1;
for (i = 1; i <= years; ++i)
{
total += (tab[1] + tab[3] - tab[4]);
//printf("%d years later total = %I64dn", i, total);
memmove(tab + 1, tab, (sizeof(tab) / sizeof(tab[0]) - 1) * sizeof(tab[0]));
tab[0] = tab[2] + tab[4];
}
return total;
}
size_t func1(int years)
{
size_t i, tab[5] = {1};
for (i = 1; i <= years; ++i)
{
memmove(tab + 1, tab, (sizeof(tab) / sizeof(tab[0]) - 1) * sizeof(tab[0]));
tab[0] = tab[2] + tab[4];
}
return tab[0] + tab[1] + tab[2] + tab[3] + tab[4];
}
double GetCowCnt(int n)
{
if (n%2 == 1)
{
return (pow((1+sqrt(5.0)) / 2, (n+1)/2+1) - pow((1-sqrt(5.0)) / 2, (n+1)/2+1)) / sqrt(5.0);
}
else
{
return 2*((pow((1+sqrt(5.0)) / 2, (n)/2+1) - pow((1-sqrt(5.0)) / 2, (n)/2+1)) / sqrt(5.0));
}
}
例题2
大数学家高斯有个好习惯:无论如何都要记日记。
他的日记有个与众不同的地方,他从不注明年月日,而是用一个整数代替,比如:4210
后来人们知道,那个整数就是日期,它表示那一天是高斯出生后的第几天。这或许也是个好习惯,它时时刻刻提醒着主人:日子又过去一天,还有多少时光可以用于浪费呢?
高斯出生于:1777年4月30日。
高斯获得博士学位的那天日记上标着:8113
请你算出高斯获得博士学位的年月日。
分析 :日记做为对象,日期是属性,翻页作为方法。
class CDiary
{
public:
int iYear;
int iMonth;
int iDay;
CDiary()
{
iYear = 1777;
iMonth = 4;
iDay = 30;
}
void PassDay()
{
iDay++;
int iMonthDay = 31;
if (iMonth == 4 || iMonth == 6 || iMonth == 9 || iMonth == 11)
{
iMonthDay = 30;
}
if (iMonth == 2)
{
int bLeapYear = iYear % 100 == 0 ? iYear % 400 == 0 : iYear % 4 == 0;
iMonthDay = bLeapYear + 28;
}
if (iDay > iMonthDay)
{
iDay = 1;
iMonth++;
if (iMonth > 12)
{
iMonth = 1;
iYear++;
}
}
}
};
void main()
{
CDiary di;
for (int i = 1; i < 8113; i++)
{
di.PassDay();
}
int j = 0;
}
1.6.2 决定对象的粒度
1.6.3 指定对象接口
对象声明的每一个操作指定操作名、作为参数的对象和返回值,这就是所谓的操作的型构( s i g n a t u r e )。对象操作所定义的所有操作型构的集合被称为该对象的接口( i n t e r f a c e )。对象接口描述了该对象所能接受的全部请求的集合,任何匹配对象接口中型构的请求都可以发送给该对象。