控制台RPG开发教程9: 基于类的代码重构


本次教程的内容:

  • 面向对象的思想
  • 类的创建与使用
  • 代码重构

程序语言也是一种语言,语言都是用来表达思想的。
编程的本质也就是表达思想。

到目前为止,我们已经了解了控制台世界的大部分功能,并且学习了c++语言大部分常用的语法知识。
我们用这些知识,实现了一个地图工作组的基本工作:

  • 1、可以显示地图
  • 2、可以在当前显示英雄
  • 3、可以接收键盘信息,并根据键盘信息来移动英雄

这个地图工作组为了正常工作,必须记录两部分信息:

  • 1、地图的信息,包括地图的展示效果,以及哪里可以走,哪里不可以走。
  • 2、英雄的当前位置。

但目前,大部分的代码(除了定位光标和隐藏光标两个函数)都是放在main函数中运行的。
也就是说,目前,它是精灵的全部工作,回想一下教程2中的RPG模型,这距离我们的设计目标还很遥远。
我们这次,将把这些代码改造为一个真正的工作组。
应该学习控制工作组的样子:把自己能做的事情说清楚,然后等待召唤就可以了。

前面的课程中讲过,c++语言是对c语言的扩展,下面我们就来了解其中一个比较显著的扩展:面向对象思想的引入。

编程语言里所谓的对象,就是我们所说的东西、事物。可以说,一切都是对象。
但在我们这里不谈太多理论,具体地说,地图工作组就是一个对象。
任何对象,都包含两部分内容:

  • 1、它会做什么

    用函数来表示:含义它能够接几种不同的电话指令。
    注意,有些电话指令是对外的,即这个工作组可以对外做什么工作。
    而有些工作是对内服务的,即是给同组的其他部门服务的。

  • 2、它必须记住什么,用变量来表示

比如我们的地图工作组,它们必须记住的信息有
    string mapInfo[5];   // 地图信息
    int x, y;            // 英雄位置
    至于
    int a;               // 每次按键的信息
    因为每次都会改变,它就不属于必须记住的信息
    
    
我们的地图工作组会做的事情,我们上面有一个总结,但在以前的代码把它们写在了一起并没有做出明确的区分。
现在我们来把它明确化一下。

  • 功能1,显示地图,void showMap()
  • 功能2,显示英雄,void showHero()
  • 功能3,监听键盘,void listen()

接下来,我们还得了解关于面向对象的一个基本概念:“对象和类”的关系。
一个比较好玩的游戏,不应该只有一张地图,所以,我们不应该只有一个地图工作组。
每个地图工作组,我们把它都看为是一个对象。
我们可以说,所有这些地图工作组都属于同一类型,这个类型,我们叫它地图工作组类型。或则简称为地图工作组类。
我们在命名上加以区分:当我们说地图工作组类的时候,我们用首字母大写的单词Map来表示,但说到具体的一个地图工作组的时候,我们用全小写的map来表示,并且后面一个跟数字,比如map1, map2。

我们的做法是,首先创建一个地图工作组类型。然后可以创造多个这种类型的工作组。当然我们现在只有一张地图,所以只创建一个工作组,也是没有问题的。
对于每个工作组来说,它们都会被分配大楼中的一个自定义款的独立房间,可以使用这个房间中构建众多的小房间,每个小房间上都会贴一个不同的标签,用来存储信息。如果想访问这些具体的小房间,则必须在前面加上对象名和一个点号标记,后面是小房间的名字。
同理,每个类型的工作组所响应的电话指令都是相同的,如果想给不同的工作组打电话,也必须在函数前面加上对象名和点号标记。

有了上面的信息,我们可以开始真正地创建地图工作组类了。
在c++中创建一个类,一般使用class语句。完整的代码见下面。

# include <iostream>
# include <conio.h>
# include <windows.h>
using namespace std;

void HideCursor(){
    CONSOLE_CURSOR_INFO cursor_info = {1, 0};
    SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}

void gotoxy(unsigned char x,unsigned char y){
    COORD cor;
    HANDLE hout;
    cor.X = x;
    cor.Y = y;
    hout = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleCursorPosition(hout, cor);
}

// 这里,定义了一个类型:地图类
class Map{
  public:
    string mapInfo[5];
    int x, y;    
    void Init(){
        mapInfo[0]= "■■■■■■■■■■■■";
        mapInfo[1]= "        ■■          ■";
        mapInfo[2]= "■  ■    ■■  ■■  ■";
        mapInfo[3]= "■  ■■        ■      ";
        mapInfo[4]= "■■■■■■■■■■■■";
        x= 0;
        y= 1;
    }
    void showMap(){
        for (int i=0; i< 5; i++){
            cout << mapInfo[i]<< endl;
        }
    }
    void showHero(){
        gotoxy(x,y);
        cout << "♀";
    }
    void listen(){
        int a;
        while(1){
            a= getch();
            if (a==72) {
               // 向上 
               if (mapInfo[y- 1][x]== ' ') {
                   gotoxy(x,y);
                   cout << "  ";
                   y= y- 1;
                   gotoxy(x,y);
                   cout << "♀";
               }
            }
            if (a==80) {
               // 向下 
               if (mapInfo[y+ 1][x]== ' ') {
                   gotoxy(x,y);
                   cout << "  ";
                   y= y+ 1;
                   gotoxy(x,y);
                   cout << "♀";
               }
            }
            if (a==75) {
               // 向左 
               if (mapInfo[y][x- 2]== ' ') {
                   gotoxy(x,y);
                   cout << "  ";
                   x= x- 2;
                   gotoxy(x,y);
                   cout << "♀";
               } 
            }
            if (a==77) {
               // 向右 
               if (mapInfo[y][x+ 2]== ' ') {
                   gotoxy(x,y);
                   cout << "  ";
                   x= x+ 2;
                   gotoxy(x,y);
                   cout << "♀";
               } 
            }
        }
    }
}; 
int main(){
    HideCursor(); 
    // 这里,创建了这个地图类型的一个具体变量(工作组)
    Map map1;
    map1.Init();
    map1.showMap();
    map1.showHero();
    map1.listen();
}

我们先看类的创建。
同class关键字定义一个类的名字,我们这里叫做Map,后面的大括号中就是这个类所必须记住的信息以及它所能做到的事情。
注意public:的标记
它的意思是说后面的所有信息都能被工作组外的代码自由访问,我们的地图工作组一切公开没有秘密。作为验证,当你在主程序输入map1.的一瞬间,你会看到编辑器帮我们列出了地图工作组的所有数据和函数,我们可以访问任何一个内容。

但我们须知,这种完全的公开坦白,在编程世界,却并不是受欢迎的行为。以后很快还会讲到这一点。

工作组的数据定义,前面讲过,原来独立的房间分配,现在都移动到了给工作组分配的大房间中,成为了套间。名字标签完全一样。
第一个函数,初始化了地图的数据和英雄的初始位置。
后面的函数,就是前面代码功能的拆分。
每个函数的返回值类型都是void,即调用它(给它打电话)的人,只关心这个指令是否完成了,而并不关心会有什么结果返回。

再看主程序main明显地变短了,大部分的工作都以及分给了地图工作组。
除了第一句隐藏光标还是自己在做,第二句用地图工作组类型创建一个工作组对象map1。从形式上看,和给大楼的一个房间贴自定义的标签没什么不同。但区别是,一般的自定义房间的住户是不会响应精灵电话的。

剩下的所有具体工作都交给了map1去做。
我们的精灵只打了4个电话:

  • 电话1:

精灵:map1,请初始化。
map1:初始化完毕。我们初始化了地图的数据和英雄的初始位置...
(精灵已挂机)
精灵(自言自语):谢谢你说的这么详细,不过我并不关心。

  • 电话2:

精灵:请显示地图。
map1:显示完毕。我们是这样显示的...
(精灵已挂机)

  • 电话3:

精灵:请显示英雄。
map1:显示完毕...
(精灵已挂机)

  • 电话4:

精灵:请开始监听键盘。
map1:键盘监听中。
精灵:我在线等。
精灵(自言自语):其实我知道什么也不会等到。这个指令永远也不会结束,如果用户想退出,他们只能关闭程序,那个时候,我们这个精灵大楼也就消失了。

我们可以看到,大部分代码和前一版是完全一样的,只不过在这里把它们根据不同的功能做了一个划分。
很高兴我们的地图工作组开始运作了,我们下节课会看到它的工作方式还有很大的优化余地。


课程小结:

本节课讲类的概念,是编程思想中一个比较关键的概念,希望大家认真理解。


 

发布了24 篇原创文章 · 获赞 0 · 访问量 4573

猜你喜欢

转载自blog.csdn.net/xiaorang/article/details/104856815