上篇博客主要介绍了对于一个类的声明,构造属性和方法。这篇博客中要介绍多个类时,如何处理类与类之间的关系。
一、继承
上回(就是上篇博客)说到声明了一个猫类,如果再声明一个狗类可以直接将Cat改为Dog,叫声“喵”改为“汪”,就可以了。同样的如果再加上猪、牛、羊类的话,也可以直接改名字和叫声就可以了。但是这样就会造成相同的代码重复写很多遍。我们当然不会想要做机器要做的事情。前人已经提出了解决办法那就是继承。先抽象出猫、狗、猪、牛、羊这些动物的共同点,将这些共同点尽量都写在父类中。显然它们都属于动物,我们就将父类定义为动物类。
不用继承的话,如果要修改功能,就必须在所有重复的方法中修改,很有可能改了这个忘了那个,代码越多,出错的可能就越大,而继承的优点是,继承使得所有子类公共的部分都放在了父类,使得代码得到了共享,这就避免了重复,另外,继承可使得修改或扩展继承而来的实现都较为容易。
class Animal//声明Animal类
{
protected string name = "";//修饰符改为了protect
public Animal(string name)
{
this.name = name;
}
public Animal()
{
this.name = "无名";
}
protected int shoutNum = 3;//修饰符改为了protect
public int ShoutNum
{
get
{
return shoutNum;
}
set
{
shoutNum = value;
}
}
}
class Cat :Animal //继承格式是子类:父类
{
public Cat()
:base() //子类构造方法需要调用父类同样参数类型的构造方法,用base关键字代表父类
{ }
public Cat(string name)
: base(name)
{ }
public string Shout() //声明猫叫的方法
{
string result = ""; //声明字符串类型的result,用来记录猫叫次数的结果
for (int i = 0; i < shoutNum; i++)
result += "喵 ";
return "我的名字叫" + name + "" + result;
}
}
class Dog : Animal
{
public Dog()
: base()
{ }
public Dog(string name)
: base(name)
{ }
public string Shout()
{
string result = "";
for (int i = 0; i < shoutNum; i++)
result += "汪";
return "我的名字叫" + name + "" + result;
}
}
private void button1_Click(object sender, EventArgs e)
{
Cat cat = new Cat("咪咪");
cat.ShoutNum = 3;
MessageBox.Show(cat.Shout());
}
private void button2_Click(object sender, EventArgs e)
{
Dog dog = new Dog("大黄");
dog.ShoutNum = 3;
MessageBox.Show(dog.Shout());
}
运行结果如下
二、多态
不知道大家在敲完的上面的代码之后有没有想过,为什么不把Shout()方法也写到父类中,其中的大部分代码也都是重复的呀?它们只是叫声不同,猫是“喵”,狗是“汪”。这就涉及到多态了,相同的方法或操作作用到不同的对象身上会产生不同的效果,多态说起来的时候好像都懂,但是我们要学习的是写代码的时候到底怎么用呢?
这就需要我们了解虚方法和方法重写。我的理解虚方法就是在父类中声明这些方法但是父类并不用,这个方法对于父类来说就形同虚设而是留给子类用的。这样即使父类中不用这些方法,但是子类可以继承这些方法。需要用virtual关键字来实现。而子类再通过方法重写就可以使用父类中的虚方法了,虚方法要用override的关键字来写。刚开始我觉得是在Animal类中声明了shout()可以在dog和cat中可以直接用,但是并不是这样的,它只是在Animal类中加入了下面的代码
public virtual string Shout()
{
return "";
}
而在dog和cat类中加入override关键字。
public override string Shout()
其他代码部分基本相同,dog和cat在继承Animal类时还是要重写shout()方法,并没有解决上面的问题(这个问题将在重构中解决)这样并没有体现写的代码少了的优势,相反的还多写了几行代码,不知道有什么优势。要深刻理解多态的好处,还要多多研究设计模式。要非得说有什么用,我想多态的作用就是实例化类时更方便一些吧。所以有了书上的例子,增加一个“动物报名”的按钮,多几个小猫小狗来报名,再增加一个“叫声比赛“的按钮这样这些小猫小狗在使用shout()方法时就会更方便一些吧。
如下面的例子
private Animal[] arrayAnimal;
//声明一个动物数组
//“动物报名”的按钮事件
private void button3_Click(object sender, EventArgs e)
{
arrayAnimal = new Animal[5];//实例化最多可报名5个的动物数组对象
arrayAnimal[0] = new Cat("小花");
arrayAnimal[1] = new Dog("阿毛");
arrayAnimal[2] = new Dog("小黑");
arrayAnimal[3] = new Cat("娇娇");
arrayAnimal[4] = new Cat("咪咪");//报名的分别是猫、狗、狗、猫、猫
}
//“叫声比赛“的按钮事件
private void button4_Click(object sender, EventArgs e)
{
foreach (Animal item in arrayAnimal)//遍历这个数组,让它们都Shout()
{
MessageBox.Show(item.Shout());//由于有了多态性,所以叫的时候,程序会自动去找item是什么对象,然后用那个重写方法
}
}
运行结果
三、重构
在多态中是加了virtual虚拟方法和override方法的重写,但是并没有实现像上面说的将cat和dog中重复的方法写到animal中。我想重构就像是字面说的,重新架构我们的代码,也许第一遍写代码时需要的功能已经能实现,但是会有很多冗余和缺陷,通过重构,使我们的代码得到优化。
优化后,可以将shout()方法写入Animal类中,在animal类中加入下面的代码
public string Shout()//去掉Virtual,成为普通的公共方法
{
string result = "";
for (int i = 0; i < shoutNum; i++) ;
result += getShoutSound() + ", ";
//此处是原先子类的唯一不同之处,所以改成调用一个虚方法getShoutSound
return "我的名字叫" + name +"" + result ;
}
protected virtual string getShoutSound()//"得到叫声",虚方法,让子类重写,只需给继承的子类使用,所以用protected修饰符
{
return "";
}
那么子类就非常简单了,所有重复都转移到了父类!
class Cat : Animal //继承格式是子类:父类
{
public Cat()
: base() //子类构造方法需要调用父类同样参数类型的构造方法,用base关键字代表父类
{ }
public Cat(string name)
: base(name)
{ }
protected override string getShoutSound()//重写getShoutSound()方法
{
return "喵";
}
}
总之:面向对象就是为了实现让我们的代码更加优化。