《重构:改善既有代码的设计》中提到过很多重构方法,关于重新组织数据的方法有16种。本文介绍:
封装集合 encapsulate collection
- 名称:封装集合 encapsulate collection
- 概要:有个函数返回一个集合。让这个函数返回该集合的一个只读副本,并在这个类中提供添加/移除集合元素的函数
- 动机:对用户暴露过多对象内部数据结构的信息。不应该额外i这整个集合提供一个设值函数,应该提供为集合添加/移除元素的函数
- 做法:
- 加入为集合添加/移除元素的函数
- 将保存集合的字段初始化为一个空集合
- 编译
- 找出集合设值函数的所有调用者。可以修改设值函数,让它使用上述“添加/移除元素”函数。也可以直接修改调用端,改让它们调用新建立的“添加/移除元素”函数
- 编译,测试
- 找出所有“通过取值函数获得集合斌修改其内容”的函数。逐一修改这些函数,让他们改用“添加/移除元素”函数
- 将get集合函数的返回值修改为只读副本
- 编译,测试
- 代码演示
修改之前的代码:
/.h
class Course
{
public:
Course(QString name, bool isAdvanced);
bool isAdvanced();
private:
QString m_name;
bool m_isAdvanced;
};
class student
{
public:
QSet<Course *>& getCourseSet() ;
void setCourseSet(QSet<Course *> pCourseSet);
private:
QSet<Course *> m_pCourseSet;
};
//.cpp
Course::Course(QString name, bool isAdvanced)
{
m_name = name;
m_isAdvanced = isAdvanced;
}
bool Course::isAdvanced()
{
return m_isAdvanced;
}
QSet<Course *>& student::getCourseSet()
{
return m_pCourseSet;
}
void student::setCourseSet(QSet<Course *> pCourseSet)
{
m_pCourseSet = pCourseSet;
}
//main.cpp
student kent;
QSet<Course*> s;
s.insert(new Course("Smalltalk Programming", false));
s.insert(new Course("Appreciateing Single Malts", true));
kent.setCourseSet(s);
qDebug() << "course size = " << kent.getCourseSet().size();
Course * pRefactor = new Course("Refactoring", true);
kent.getCourseSet().insert(pRefactor);
kent.getCourseSet().insert(new Course("Brutal Sarcasm", false));
qDebug() << "course size = " << kent.getCourseSet().size();
kent.getCourseSet().remove(pRefactor);
qDebug() << "course size = " << kent.getCourseSet().size();
int count = 0;
for (Course* c : kent.getCourseSet())
{
if (c->isAdvanced() == true)
{
count++;
}
}
qDebug() << "Advanced course count is " << count;
执行结果:
kent.getCourseSet()相关的操作,将student类内部数据暴露了。修改如下:
1) 为student中的集合建立合适的添加/删除函数
2)观察设值函数,如果有很多地点大量运用了设值函数,就需要修改设值函数,令它调用添加/移除函数。在本文中,setCourseSet()仅仅在main.cpp中使用过一次,调用之前,是个空集合。仅仅用来初始化。所以修改setCourseSet()内容为调用addCourse(),并使用rename method修改为initializeCourse().
3) 使用addCourse()和removeCourse()来增减课程
4)让getCourse()返回一个只读副本
5)main.cpp中计算高级课只使用了student的数据,所以使用extract method将这段代码提炼为一个独立函数。然后使用mobe method将其搬移到student类中。
6)将kent.getCourseSet().size() 修改为更具可读性的
修改之后的代码:
/.h
class Course
{
public:
Course(QString name, bool isAdvanced);
bool isAdvanced();
private:
QString m_name;
bool m_isAdvanced;
};
class student
{
public:
const QSet<Course *> getCourseSet() ;
void initializeCourse(QSet<Course *> pCourseSet);
void addCourse(Course *pCourse);
void removeCourse(Course *pCourse);
int numberOfAdvancedCourses();
int numberOfCourses();
private:
QSet<Course *> m_pCourseSet;
};
//.cpp
Course::Course(QString name, bool isAdvanced)
{
m_name = name;
m_isAdvanced = isAdvanced;
}
bool Course::isAdvanced()
{
return m_isAdvanced;
}
const QSet<Course *> student::getCourseSet()
{
return m_pCourseSet;
}
void student::initializeCourse(QSet<Course *> pCourseSet)
{
for (Course *pCourse : pCourseSet)
{
m_pCourseSet.insert(pCourse);
}
}
void student::addCourse(Course *pCourse)
{
m_pCourseSet.insert(pCourse);
}
void student::removeCourse(Course *pCourse)
{
m_pCourseSet.remove(pCourse);
}
int student::numberOfAdvancedCourses()
{
int result = 0;
for (Course* c : m_pCourseSet)
{
if (c->isAdvanced() == true)
{
result++;
}
}
return result;
}
int student::numberOfCourses()
{
return m_pCourseSet.size();
}
///main.cpp
student kent;
QSet<Course*> s;
s.insert(new Course("Smalltalk Programming", false));
s.insert(new Course("Appreciateing Single Malts", true));
kent.initializeCourse(s);
qDebug() << "course size = " << kent.numberOfCourses();
Course * pRefactor = new Course("Refactoring", true);
kent.addCourse(pRefactor);
kent.addCourse(new Course("Brutal Sarcasm", false));
qDebug() << "course size = " << kent.numberOfCourses();
kent.removeCourse(pRefactor);
qDebug() << "course size = " << kent.numberOfCourses();
qDebug() << "Advanced course count is " << kent.numberOfAdvancedCourses();