实验4 组合与继承
实验任务1
程序源代码如下:
GradeCalc.hpp
1 #pragma once 2 3 #include <vector> 4 #include <array> 5 #include <string> 6 7 class GradeCalc { 8 public: 9 GradeCalc(const std::string& cname); 10 void input(int n); 11 void output() const; 12 void sort(bool ascending = false); 13 int min() const; 14 int max() const; 15 double average() const; 16 void info(); 17 18 private: 19 void compute(); 20 21 private: 22 std::string course_name; 23 std::vector<int> grades; 24 std::array<int, 5> counts; 25 std::array<double, 5> rates; 26 bool is_dirty; 27 };
GradeCalc.cpp
1 #include <algorithm> 2 #include <array> 3 #include <cstdlib> 4 #include <iomanip> 5 #include <iostream> 6 #include <numeric> 7 #include <string> 8 #include <vector> 9 10 #include "GradeCalc.hpp" 11 12 GradeCalc::GradeCalc(const std::string& cname) :course_name{ cname }, is_dirty{ true } { 13 counts.fill(0); 14 rates.fill(0); 15 } 16 17 void GradeCalc::input(int n) { 18 if (n < 0) { 19 std::cerr << "无效输入! 人数不能为负数\n"; 20 std::exit(1); 21 } 22 23 grades.reserve(n); 24 25 int grade; 26 27 for (int i = 0; i < n;) { 28 std::cin >> grade; 29 30 if (grade < 0 || grade > 100) { 31 std::cerr << "无效输入! 分数须在[0,100]\n"; 32 continue; 33 } 34 35 grades.push_back(grade); 36 ++i; 37 } 38 39 is_dirty = true; 40 } 41 42 void GradeCalc::output() const { 43 for (auto grade : grades) 44 std::cout << grade << ' '; 45 std::cout << std::endl; 46 } 47 48 void GradeCalc::sort(bool ascending) { 49 if (ascending) 50 std::sort(grades.begin(), grades.end()); 51 else 52 std::sort(grades.begin(), grades.end(), std::greater<int>()); 53 } 54 55 int GradeCalc::min() const { 56 if (grades.empty()) 57 return -1; 58 59 auto it = std::min_element(grades.begin(), grades.end()); 60 return *it; 61 } 62 63 int GradeCalc::max() const { 64 if (grades.empty()) 65 return -1; 66 67 auto it = std::max_element(grades.begin(), grades.end()); 68 return *it; 69 } 70 71 double GradeCalc::average() const { 72 if (grades.empty()) 73 return 0.0; 74 75 double avg = std::accumulate(grades.begin(), grades.end(), 0.0) / grades.size(); 76 return avg; 77 } 78 79 void GradeCalc::info() { 80 if (is_dirty) 81 compute(); 82 83 std::cout << "课程名称:\t" << course_name << std::endl; 84 std::cout << "平均分:\t" << std::fixed << std::setprecision(2) << average() << std::endl; 85 std::cout << "最高分:\t" << max() << std::endl; 86 std::cout << "最低分:\t" << min() << std::endl; 87 88 const std::array<std::string, 5> grade_range{ "[0, 60) ", 89 "[60, 70)", 90 "[70, 80)", 91 "[80, 90)", 92 "[90, 100]" }; 93 94 for (int i = static_cast<int>(grade_range.size()) - 1; i >= 0; --i) 95 std::cout << grade_range[i] << "\t: " << counts[i] << "人\t" 96 << std::fixed << std::setprecision(2) << rates[i] * 100 << "%\n"; 97 } 98 99 void GradeCalc::compute() { 100 if (grades.empty()) 101 return; 102 103 counts.fill(0); 104 rates.fill(0.0); 105 106 for (auto grade : grades) { 107 if (grade < 60) 108 ++counts[0]; 109 else if (grade < 70) 110 ++counts[1]; 111 else if (grade < 80) 112 ++counts[2]; 113 else if (grade < 90) 114 ++counts[3]; 115 else 116 ++counts[4]; 117 } 118 119 for (size_t i = 0; i < rates.size(); ++i) 120 rates[i] = counts[i] * 1.0 / grades.size(); 121 122 is_dirty = false; 123 }
task1.cpp
1 #include <iostream> 2 #include <string> 3 #include "GradeCalc.hpp" 4 5 void test() { 6 GradeCalc c1("OOP"); 7 8 std::cout << "录入成绩:\n"; 9 c1.input(5); 10 11 std::cout << "输出成绩:\n"; 12 c1.output(); 13 14 std::cout << "排序后成绩:\n"; 15 c1.sort(); c1.output(); 16 17 std::cout << "*************成绩统计信息*************\n"; 18 c1.info(); 19 20 } 21 22 int main() { 23 test(); 24 }
程序运行结果如下所示:

问题1:GradeCalc 类声明中,逐行写出所有体现"组合"关系的成员声明,并用一句话说明每个被组合对象的功能。
std::string course_name:用于存储课程名称
std::vector<int> grades:用于存储每名学生这门课程的成绩
std::array<int, 5> counts:用于存储各分数段学生的人数
std::array<double, 5> rates:用于存储各分数段学生人数的占比
问题2:如在 test 模块中这样使用,是否合法?如不合法,解释原因。
GradeCalc c("OOP");
c.input(5);
c.push_back(97); // 合法吗?
不合法,push_back()函数是vector容器的函数,对象c中的vector对象grades是私有成员,类外部不可以直接访问,故不合法
问题3:当前设计方案中, compute 在 info 模块中调用:
(1)连续打印3次统计信息, compute 会被调用几次?标记 is_dirty 起到什么作用?
compute只会被调用1次,因为调用一次之后,is_dirty被赋值为false,接下来再次进行信息打印时,compute不会被调用
is_dirty作用是标记当前状态下成绩是否已经被统计过,若当前状态下成绩已被统计过,接下来打印信息时不重复统计,若成绩信息有变更,is_dirty值改变,表示当前状态未被统计过,就需要重新对成绩信息进行统计
(2)如新增 update_grade(index, new_grade) ,这种设计需要更改 compute 调用位置吗?简洁说明理由。
不需要,compute是否调用是与is_dirty的值有关的,而非和调用位置有关,只需要在updata_grade中进行修改is_dirty的值,即可实现compute的调用。
问题4:要增加"中位数"统计,不新增数据成员怎么做?在哪个函数里加?写出伪代码。
可以在sort函数中加入这个功能,把函数返回值类型改为double,完成排序后
if(grades.size()%2!=0){return grades[grades.size()/2];}
else{return (grades[grades.size()/2-1]+grades[grades.size()/2])/2;}
问题5:GradeCalc 和 compute 中都包含代码: counts.fill(0); rates.fill(0); compute 中能否去掉这两行?如去掉,在哪种使用场景下会引发统计错误?
不可以去掉,如果把这两行去掉,相当于失去了这两个统计数组的重置功能,那么当已经进行了一次成绩的统计,要进行第二次成绩统计时,count值会在原来数据基础上进行累加,导致统计结果的错误
问题6:input 模块中代码 grades.reserve(n); 如果去掉:
(1)对程序功能有影响吗?(去掉重新编译、运行,观察功能是否受影响)
没有影响,注释此行后,程序依旧可以正常运行。
(2)对性能有影响吗?如有影响,用一句话陈述具体影响。
有影响。若不提前分配好足够的空间,vector容器会不断分配新的内存空间,导致性能降低
实验任务2
实验源代码如下:
GradeCalc.hpp
1 #pragma once 2 3 #include <array> 4 #include <string> 5 #include <vector> 6 7 class GradeCalc : private std::vector<int> { 8 public: 9 GradeCalc(const std::string& cname); 10 void input(int n); 11 void output() const; 12 void sort(bool ascending = false); 13 int min() const; 14 int max() const; 15 double average() const; 16 void info(); 17 18 private: 19 void compute(); 20 21 private: 22 std::string course_name; 23 std::array<int, 5> counts; 24 std::array<double, 5> rates; 25 bool is_dirty; 26 };
GradeCalc.cpp
1 #include <algorithm> 2 #include <array> 3 #include <cstdlib> 4 #include <iomanip> 5 #include <iostream> 6 #include <numeric> 7 #include <string> 8 #include <vector> 9 #include "GradeCalc.hpp" 10 11 12 GradeCalc::GradeCalc(const std::string& cname) : course_name{ cname }, is_dirty{ true } { 13 counts.fill(0); 14 rates.fill(0); 15 } 16 17 void GradeCalc::input(int n) { 18 if (n < 0) { 19 std::cerr << "无效输入! 人数不能为负数\n"; 20 return; 21 } 22 23 this->reserve(n); 24 25 int grade; 26 27 for (int i = 0; i < n;) { 28 std::cin >> grade; 29 if (grade < 0 || grade > 100) { 30 std::cerr << "无效输入! 分数须在[0,100]\n"; 31 continue; 32 } 33 34 this->push_back(grade); 35 ++i; 36 } 37 38 is_dirty = true; 39 } 40 41 void GradeCalc::output() const { 42 for (auto grade : *this) 43 std::cout << grade << ' '; 44 std::cout << std::endl; 45 } 46 47 void GradeCalc::sort(bool ascending) { 48 if (ascending) 49 std::sort(this->begin(), this->end()); 50 else 51 std::sort(this->begin(), this->end(), std::greater<int>()); 52 } 53 54 int GradeCalc::min() const { 55 if (this->empty()) 56 return -1; 57 58 return *std::min_element(this->begin(), this->end()); 59 } 60 61 int GradeCalc::max() const { 62 if (this->empty()) 63 return -1; 64 65 return *std::max_element(this->begin(), this->end()); 66 } 67 68 double GradeCalc::average() const { 69 if (this->empty()) 70 return 0.0; 71 72 double avg = std::accumulate(this->begin(), this->end(), 0.0) / this->size(); 73 return avg; 74 } 75 76 void GradeCalc::info() { 77 if (is_dirty) 78 compute(); 79 80 std::cout << "课程名称:\t" << course_name << std::endl; 81 std::cout << "平均分:\t" << std::fixed << std::setprecision(2) << average() << std::endl; 82 std::cout << "最高分:\t" << max() << std::endl; 83 std::cout << "最低分:\t" << min() << std::endl; 84 85 const std::array<std::string, 5> grade_range{ "[0, 60) ", 86 "[60, 70)", 87 "[70, 80)", 88 "[80, 90)", 89 "[90, 100]" }; 90 91 for (int i = static_cast<int>(grade_range.size()) - 1; i >= 0; --i) 92 std::cout << grade_range[i] << "\t: " << counts[i] << "人\t" 93 << std::fixed << std::setprecision(2) << rates[i] * 100 << "%\n"; 94 } 95 96 void GradeCalc::compute() { 97 if (this->empty()) 98 return; 99 100 counts.fill(0); 101 rates.fill(0); 102 103 for (int grade : *this) { 104 if (grade < 60) 105 ++counts[0]; 106 else if (grade < 70) 107 ++counts[1]; 108 else if (grade < 80) 109 ++counts[2]; 110 else if (grade < 90) 111 ++counts[3]; 112 else 113 ++counts[4]; 114 } 115 116 for (size_t i = 0; i < rates.size(); ++i) 117 rates[i] = counts[i] * 1.0 / this->size(); 118 119 is_dirty = false; 120 }
task2.cpp
1 #include <iostream> 2 #include <string> 3 #include "GradeCalc.hpp" 4 5 void test() { 6 GradeCalc c1("OOP"); 7 8 std::cout << "录入成绩:\n"; 9 c1.input(5); 10 11 std::cout << "输出成绩:\n"; 12 c1.output(); 13 14 std::cout << "排序后成绩:\n"; 15 c1.sort(); c1.output(); 16 17 std::cout << "*************成绩统计信息*************\n"; 18 c1.info(); 19 20 } 21 22 int main() { 23 test(); 24 }
程序运行结果如下:

问题1:写出 GradeCalc 类声明体现"继承"关系的完整代码行。
class GradeCalc : private std::vector<int>
问题2:当前继承方式下,基类 vector<int> 的接口会自动成为 GradeCalc 的接口吗?如在 test 模块中这样用,能否编译通过?用一句话解释原因。
GradeCalc c("OOP");
c.input(5);
c.push_back(97); // 合法吗?
不会自动成为GradeCalc的接口,因为GradeCalc以private方式继承vector类,其内部接口的访问权限为private,在vector类外部不能直接访问
编译不能通过,在类外部直接访问了访问权限为私有的函数,会产生错误
问题3:
对比继承方式与组合方式内部实现数据访问的一行典型代码。说明两种方式下的封装差异带来的数据访问接口差异。
// 组合方式
for(auto grade: grades) // 通过什么接口访问数据
// 略
// 继承方式
for(int grade: *this) // 通过什么接口访问数据
// 略
组合方式中,利用当前类中的成员变量grades访问数据,其数据被封装在组合类中,当前类不能直接访问这些数据,只能通过组合类对象进行访问
继承方式中,利用当前类的成员的this指针直接访问数据,继承后派生类可以访问基类的数据
组合方式封装性更强,需要通过当前类中的成员变量去间接访问数据,继承方式封装性稍弱,可直接通过当前派生类中的this指针去访问基类数据
问题4:你认为组合方案和继承方案,哪个更适合成绩计算这个问题场景?简洁陈述你的结论和理由。
我认为组合更适合这个问题场景
1.组合方式封装性更强,只能通过组合类成员对数据进行访问而不会直接接触数据,安全性更好
2.组合方式更符合现实认知,成绩统计中,应该是需要一个vector类容器作为工具来进行成绩统计,二者是一个包含关系,而非成绩统计继承于vector类
3.组合方式更便于后期的修改,若后期需要更换存储容器,只需要修改组合类成员及其对应的访问方式即可,而继承方式要修改的地方相对较多
实验任务3
程序源代码如下:
Graph.hpp
1 #pragma once 2 3 #include <string> 4 #include <vector> 5 6 enum class GraphType { circle, triangle, rectangle }; 7 8 class Graph { 9 public: 10 virtual void draw() {} 11 virtual ~Graph() = default; 12 }; 13 14 class Circle : public Graph { 15 public: 16 void draw(); 17 }; 18 19 class Triangle : public Graph { 20 public: 21 void draw(); 22 }; 23 24 class Rectangle : public Graph { 25 public: 26 void draw(); 27 }; 28 29 class Canvas { 30 public: 31 void add(const std::string& type); 32 void paint() const; 33 ~Canvas(); 34 35 private: 36 std::vector<Graph*> graphs; 37 }; 38 39 GraphType str_to_GraphType(const std::string& s); 40 Graph* make_graph(const std::string& type);
Graph.cpp
1 #include <algorithm> 2 #include <cctype> 3 #include <iostream> 4 #include <string> 5 6 #include "Graph.hpp" 7 8 void Circle::draw() { std::cout << "draw a circle...\n"; } 9 10 void Triangle::draw() { std::cout << "draw a triangle...\n"; } 11 12 void Rectangle::draw() { std::cout << "draw a rectangle...\n"; } 13 14 void Canvas::add(const std::string& type) { 15 Graph* g = make_graph(type); 16 if (g) 17 graphs.push_back(g); 18 } 19 20 void Canvas::paint() const { 21 for (Graph* g : graphs) 22 g->draw(); 23 } 24 25 Canvas::~Canvas() { 26 for (Graph* g : graphs) 27 delete g; 28 } 29 30 GraphType str_to_GraphType(const std::string& s) { 31 std::string t = s; 32 std::transform(s.begin(), s.end(), t.begin(), 33 [](unsigned char c) { return std::tolower(c); }); 34 35 if (t == "circle") 36 return GraphType::circle; 37 38 if (t == "triangle") 39 return GraphType::triangle; 40 41 if (t == "rectangle") 42 return GraphType::rectangle; 43 44 return GraphType::circle; 45 } 46 47 Graph* make_graph(const std::string& type) { 48 switch (str_to_GraphType(type)) { 49 case GraphType::circle: return new Circle; 50 case GraphType::triangle: return new Triangle; 51 case GraphType::rectangle: return new Rectangle; 52 default: return nullptr; 53 } 54 }
task3.cpp
#include <string> #include "Graph.hpp" void test() { Canvas canvas; canvas.add("circle"); canvas.add("triangle"); canvas.add("rectangle"); canvas.paint(); } int main() { test(); }
程序运行结果截图如下:

问题1:
(1)写出Graph.hpp中体现"组合"关系的成员声明代码行,并用一句话说明被组合对象的功能。
std::vector<Graph*> graphs;:用于存储指向图类对象的指针
(2)写出Graph.hpp中体现"继承"关系的类声明代码行。
class Circle : public Graph
class Triangle : public Graph
class Rectangle : public Graph
问题2:
(1) Graph 中的 draw 若未声明成虚函数, Canvas::paint() 中 g->draw() 运行结果会有何不同?
运行时都会调用Graph基类中的draw函数,即没有输出
(2)若 Canvas 类 std::vector<Graph*> 改成 std::vector<Graph> ,会出现什么问题?
运行时依旧调用Graph基类中的draw函数,既没有输出,因为派生类的实例对象被创建在堆上,当vector存储为实体而非指针时,vector会尝试将派生类实例对象拷贝到vector的Graph元素中,拷贝过程中派生类的函数draw不会被拷贝到其中,所以调用时调用的是基类Graph的draw函数
(3)若 ~Graph() 未声明成虚函数,会带来什么问题?
会导致子类中的堆上创建的成员不能被析构,造成资源泄漏
问题3:
若要新增星形 Star ,需在哪些文件做哪些改动?逐一列出。
1.Graph.hpp中创建一个Star类,继承Graph类,并创建draw函数
2.Graph.hpp中的枚举类中加入star
3.Graph.cpp中加入draw函数的实现,并在str_to_GraphType函数中加入star类的返回,在make_graph中加入star类对象在堆上的创建
4.在task3.cpp中加上canvas.add("star")
问题4:观察 make_graph 函数和 Canvas 析构函数:
(1) make_graph 返回的对象在什么地方被释放?
在Canvas类的析构函数中,遍历graphs中的指针并调用delete进行释放
(2)使用原始指针管理内存有何利弊?
利:简单易理解,可实现基本的内存分配
弊:需要自行手动对堆上变量进行删除,若遗忘会造成内存的泄露
实验任务4
1.设计场景:有一些电子毛绒玩具,他们有不同的类别,将他们放入玩具工厂中,在玩具工厂中用一个接口实现对所有玩具玩法的调用
2.各类关系:基类Toy,其有派生类SoundingToy,MovingToy,InteractingToy,玩具工厂类ToyFactory,其内部有vector类的实例对象
设计理由:三类电动毛绒玩具虽然具体功能不同,但他们有一部分属性如名字,描述是共同具有的,故继承基类Toy,而玩具工厂类中需要存储各类玩具的容器,故在ToyFactory内部设计vector类实例对象
3.程序源代码如下:
Toy.hpp
1 #pragma once 2 3 #include <string> 4 5 enum class ToyType { Sounding, Moving, Interacting }; 6 7 class Toy { 8 public: 9 Toy(const std::string& _name, const std::string& _description, 10 const std::string& _material, ToyType _type, double _price); 11 12 virtual ~Toy() = default; 13 14 virtual void execute() = 0; 15 virtual void showInfo() const; 16 17 std::string getName() const; 18 19 private: 20 std::string name; 21 std::string description; 22 std::string material; 23 ToyType type; 24 double price; 25 }; 26 27 std::string ToyTypeToString(ToyType type);
MultiToy.hpp
1 #pragma once 2 3 #include "Toy.hpp" 4 5 class SoundingToy :public Toy{ 6 public: 7 SoundingToy(const std::string& _name, const std::string& _description, 8 const std::string& _material, ToyType _type, double _price, const std::string _speaking); 9 10 void execute(); 11 void showInfo(); 12 13 private: 14 std::string speaking; 15 }; 16 17 class MovingToy :public Toy { 18 public: 19 MovingToy(const std::string& _name, const std::string& _description, 20 const std::string& _material, ToyType _type, double _price, const std::string _moving); 21 22 void execute(); 23 void showInfo(); 24 25 private: 26 std::string moving; 27 }; 28 29 class InteractingToy :public Toy { 30 public: 31 InteractingToy(const std::string& _name, const std::string& _description, 32 const std::string& _material, ToyType _type, double _price, const std::string _interacting); 33 34 void execute(); 35 void showInfo(); 36 37 private: 38 std::string interacting; 39 };
ToyFactory.hpp
#pragma once #include <vector> #include "Toy.hpp" #include "MultiToy.hpp" class ToyFactory{ public: ToyFactory() = default; ~ToyFactory(); void addToy(Toy* toy); void showAllToys(); void play(); private: std::vector<Toy*> toys; static int count; };
Toy.cpp
1 #include "Toy.hpp" 2 #include <iostream> 3 4 std::string ToyTypeToString(ToyType type) { 5 switch (type) { 6 case ToyType::Sounding: return "发声类玩具"; 7 case ToyType::Moving: return "移动类玩具"; 8 case ToyType::Interacting: return "互动类玩具"; 9 } 10 } 11 12 Toy::Toy(const std::string& _name, const std::string& _description, 13 const std::string& _material, ToyType _type, double _price): 14 name{_name},description{_description},material{_material},type{_type},price{_price}{} 15 16 void Toy::showInfo() const{ 17 std::cout << "玩具名称:" << name << std::endl; 18 std::cout << "玩具描述:" << description << std::endl; 19 std::cout << "玩具材质:" << material << std::endl; 20 std::cout << "玩具种类:" << ToyTypeToString(type) << std::endl; 21 std::cout << "玩具价格:" << price << std::endl; 22 } 23 24 std::string Toy::getName() const { return name; }
MultiToy.cpp
1 #include "MultiToy.hpp" 2 #include <iostream> 3 4 SoundingToy::SoundingToy(const std::string& _name, const std::string& _description, 5 const std::string& _material, ToyType _type, double _price, const std::string _speaking): 6 Toy(_name,_description,_material,_type,_price),speaking{_speaking}{} 7 8 void SoundingToy::execute() { 9 std::cout << getName() << "说话了,它说:" << speaking; 10 } 11 12 void SoundingToy::showInfo() { 13 Toy::showInfo(); 14 std::cout << "它会说:" << speaking; 15 } 16 17 MovingToy::MovingToy(const std::string& _name, const std::string& _description, 18 const std::string& _material, ToyType _type, double _price, const std::string _moving): 19 Toy(_name, _description, _material, _type, _price), moving{ _moving } {} 20 21 void MovingToy::execute() { 22 std::cout << getName() << "活动了,它:" << moving; 23 } 24 25 void MovingToy::showInfo() { 26 Toy::showInfo(); 27 std::cout << "它可以:" << moving; 28 } 29 30 InteractingToy::InteractingToy(const std::string& _name, const std::string& _description, 31 const std::string& _material, ToyType _type, double _price, const std::string _interacting): 32 Toy(_name, _description, _material, _type, _price), interacting{ _interacting } {} 33 34 void InteractingToy::execute() { 35 std::cout << getName() << "和你互动,它:" << interacting; 36 } 37 38 void InteractingToy::showInfo() { 39 Toy::showInfo(); 40 std::cout << "它的互动方式是:" << interacting; 41 }
ToyFactory.cpp
1 #include "ToyFactory.hpp" 2 #include <iostream> 3 4 int ToyFactory::count = 0; 5 6 ToyFactory::~ToyFactory() { 7 for (auto toy : toys) { 8 delete toy; 9 } 10 } 11 12 void ToyFactory::addToy(Toy* toy) { 13 toys.push_back(toy); 14 count++; 15 } 16 17 void ToyFactory::showAllToys() { 18 if (toys.empty()) { 19 std::cout << "工厂内目前没有玩具" << std::endl; 20 } 21 else { 22 std::cout << "工厂内目前有" << count << "个玩具" << std::endl; 23 for (auto toy : toys) { 24 toy->showInfo(); 25 std::cout << std::endl; 26 } 27 } 28 } 29 30 void ToyFactory::play() { 31 if (toys.empty()) { 32 std::cout << "目前没有玩具可玩" << std::endl; 33 } 34 else { 35 for (auto toy : toys) { 36 toy->execute(); 37 std::cout << std::endl; 38 } 39 } 40 }
task4.cpp
1 #include <iostream> 2 #include "Toy.hpp" 3 #include "MultiToy.hpp" 4 #include "ToyFactory.hpp" 5 6 int main() { 7 ToyFactory factory; 8 factory.showAllToys(); 9 factory.play(); 10 std::cout << std::endl; 11 12 Toy* toy1 = new SoundingToy("旺财", "一只可爱的小狗", "涤纶", ToyType::Sounding, 100.00, "你好呀!"); 13 Toy* toy2 = new MovingToy("团子", "一只可爱的小猫", "棉", ToyType::Moving, 300.00, "快速跑"); 14 Toy* toy3 = new InteractingToy("雪球", "一只可爱的兔子", "硅胶", ToyType::Interacting, 200.00, "摸你的头"); 15 16 factory.addToy(toy1); 17 factory.addToy(toy2); 18 factory.addToy(toy3); 19 20 factory.showAllToys(); 21 factory.play(); 22 }
4.程序运行结果如下:

实验总结
通过此次实验,我明白了组合与继承两种设计方式的优势以及两者的区别,更学会了如何在不同的场景下去应用这两种设计模式
浙公网安备 33010602011771号