这节课讲的是面向对象的几个基本概念,老师举了个例子把面向对象的很多东西都连接起来了:动物比赛,每个不同动物都有名字,能发出叫声等。。。
一.继承.
第一、子类拥有父类非private的属性和功能;
第二、子类具有自己的属性和功能,即子类可以扩展父类没有的属性和功能;
第三、子类还可以以自己的方式实现父类的功能(方法重写)
除了private和public外,还有protected表示继承时子类可以对父类有完全访问权。 也就是说,protected修饰的类成员,对子类公开,对其它类不公开。
来看个例子:
猫类:
class Cat
{
private int shoutNum = 3;
public int ShoutNum
{
get
{
return shoutNum;
}
set
{
if (value < 10)
{
shoutNum = value;
}
else
{
shoutNum = 10;
}
}
}
private string name = "";
public Cat(string name)
{
this.name = name;
}
public Cat()
{
this.name = "无名";
}
public string Shout()
{
string result = "";
for (int i = 0; i < shoutNum; i++)
{
result += "喵 ";
}
return "我的名字叫" + name + " " + result;
}
}
狗类:
class Dog
{
private int shoutNum = 3;
public int ShoutNum
{
get
{
return shoutNum;
}
set
{
if (value < 10)
{
shoutNum = value;
}
else
{
shoutNum = 10;
}
}
}
private string name = "";
public Dog(string name)
{
this.name = name;
}
public Dog()
{
this.name = "无名";
}
public string Shout()
{
string result = "";
for (int i = 0; i < shoutNum; i++)
{
result += "汪 ";
}
return "我的名字叫" + name + " " + result;
}
}
我们发现这两个类有很多相同的地方,于是我们把他们相同的地方都弄出来组成一个Animal类:
class Animal
{
protected string name = "";//用了protected
public Animal(string name)
{
this.name = name;
}
public Animal()
{
this.name = "无名";
}
protected int shoutNum = 3;
public int ShoutNum
{
get
{
return shoutNum;
}
set
{
shoutNum = value;
}
}
}
于是猫狗类就可以这样写:
class Cat : Animal
{
public Cat() : base()
{ }
public Cat(string name) : base(name)
{ }
public string Shout()
{
string 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;
}
}
用UML类图表示:
继承优点:继承使得所有子类公共的部分都放在了父类,使得代码得到了共享,这就避免了重复,另外,继承可使得修改或扩展继承而来的实现都较为容易。
缺点:父类变,则子类不得不变。继承会破坏包装,父类实现细节暴露给子类,这其实是增大了两个类之间的耦合性。
虽然采用了继承,但子类还是显得有些复杂,假如现在又有很多牛,羊什么的来了,那么这个Shout()函数就会有很多种,显得有点复杂,怎样解决???
二.多态.
1.概念:表示不同的对象可以执行相同的动作,但要通过它们自己的实现代码来执行。
2.举例:
有一位父亲是有名的艺术家, 六个儿子长大成人, 模仿父亲惟妙惟肖。 有一天,父亲突然发烧,上不了台,但是票已经卖了,退票影响声誉。于是决定让儿子化了 妆代父亲表演。
有以下特点:
第一,子类以父类的身份出现
儿子代表父亲表演,化妆后就以父亲身份出现了。
第二,子类在工作时以自己的方式来实现
儿子模仿得再好,那也是模仿,儿子只能用自己方式表演父亲的作品。
第三,子类以父亲的身份出现时,子类特有的属性和方法不可以使用
儿子有自己的绝活也不能使用。
3.虚方法和方法重写
虚方法:为了使子类的实例完全接替来自父类的类成员,父类必须将该成员声明为虚拟的。这是通过在该成员的返回类型之前添加virtual关键字来实现的。
方法重写:子类可以选择使用override关键字,将父类实现替换为它自己的实现,这就是方法重写override
class Animal
{
……
public virtual string Shout()//虚方法,没有实现具体功能
{
return "";
}
}
class Cat : Animal//猫类
{
public Cat() : base()
{ }
public Cat(string name) : base(name)
{ }
public override string Shout()//方法重写
{
string 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 override string Shout()//方法重写
{
string result = "";
for (int i = 0; i < shoutNum; i++)
result += "汪 ";
return "我的名字叫" + name + " " + result;
}
}
下面看怎么实例化来运用这些类的:
private Animal[] arrayAnimal;
private void btnRigister_Click(object sender, EventArgs e)
{
arrayAnimal = new Animal[5];
arrayAnimal[0] = new Cat("小花");
arrayAnimal[1] = new Dog("阿毛");
arrayAnimal[2] = new Dog("小黑");
arrayAnimal[3] = new Cat("娇娇");
arrayAnimal[4] = new Cat(“小咪");
}
private void btnMatch_Click(object sender, EventArgs e)
{
foreach (Animal item in arrayAnimal)
{
MessageBox.Show(item.Shout());
}
}
运行结果:
来分析一下多态在内存的情况:
多态的原理是当方法被调用时,无论对象是否被转换为其父类,都只有位于对象继承链最末端的方法实现会被调用。也就是说,虚方法是按照其运行时类型而非编译时类型进行动态绑定调用的。
当mybc调用print()函数时,首先找到父类的Print,父类的print指向子类的print,最终调用的是子类的print().
所以现在来了牛羊,我们只需要这样定义类:
class Cattle : Animal
{
public Cattle() : base()
{ }
public Cattle(string name) : base(name)
{ }
public override string Shout()
{
string result = "";
for (int i = 0; i < shoutNum; i++)
result += “哞";
return "我的名字叫" + name + " " + result;
}
}
class Sheep : Animal
{
public Sheep() : base()
{ }
public Sheep(string name) : base(name)
{ }
public override string Shout()
{
string result = "";
for (int i = 0; i < shoutNum; i++)
result += “咩 ";
return "我的名字叫" + name + " " + result;
}
}
我们发现虽然这样做了,但还是没有减少子类代码的复杂度,Shout()函数还在子类里,所以我们希望把Shout()函数弄到父类中去,从而引入重构的概念!
三.重构.
1.概念:
两个互为兄弟的子类代码类似,并非完全相同首先采用Extract Method(提炼函数)区分相同部分和差异部分代码,构成单独函数然后再对提炼的代码使用Pull UpMethod(方法上移),将它移入父类
2.对上述Animal进行改进,把子类中的shout()函数(相同部分)放到父类中,在父类中增加一个虚方法(不同部分,需子类实现)
class Animal
{
……
public string Shout()
{
string result = "";
for (int i = 0; i < shoutNum; i++)
result += getShoutSound() + ",";
return "我的名字叫" + name + " " + result;
}
protected virtual string getShoutSound()
{
return "";
}
}
class Cat : Animal
{
public Cat() : base()
{ }
public Cat(string name):base(name)
{
}
protected override string getShoutSound()
{
return "喵";
}
}
class Dog : Animal
{
public Dog() : base()
{ }
public Dog(string name):base(name)
{
}
protected override string getShoutSound()
{
return "汪";
}
}
这样一来我们的目的就达成了,,由于篇幅太长,分两篇写的,请看下篇!