目录
一、面向对象核心原则概述
- 单一职责(SRP:Single reponsibility principle)
- 针对类的设计而言,对象的单一职责
- 不能设计万能类,要按照对象的基本要求,添加的属性、方法,都属于对象的本身
- 开-闭原则(OCP:Open Closed Principle)
- 使用场景:随需求变化而修改
- 开:通过增加类的方法解决需求问题而不是修改原有类,开的是增加
- 闭:对以前他人设计的类或模块最好不要轻易修改,闭的是修改
- 好处:让需求变化风险可控,让后期维护成本变小
- 里氏替换原则(LSP:Liskov Substitution Principle)
- 适用场景:使用继承,并希望使用继承实现程序更多的“动态扩展”(多态)
- 基本特点:子类可以在任何地方替换它的父类
- 使用:一般是说父类作为方法参数,方法返回类型的时候,通常是传递一个子类对象,或者返回一个子类对象
- 接口隔离原则(ISP:Interface Segregation Principle)
- 含义:接口最小原则
- 特点:要求尽量使用职能单一的接口,而不使用职能复杂、全面的接口
- 接口内容太多的后果:接口污染,使用和定义困难,维护成本高,重构系统时首先考虑接口细分
- 依赖倒置原则(DIP:Dependence Inversion Principle)
- 不倒置的情况是什么:UI->BLL->DAL(强依赖)
- 原则特点:要求调用者和被调用者都依赖“抽象”,两者没关联,互不影响
- 好处:解耦
- 实现途径:提前设计接口库,依赖接口编程,也就是调用接口,和实现接口,调用接口的不管实现,实现的不管调用,也可以通过抽象类完成
- 具体做法:接口类库、实例方法和构造方法中使用接口作为参数
- 迪米特原则(LOD:Law of Demeter)
- 面向对象三大特性:封装、继承、多态
- 如何理解:尽量最大化的封装,该用private,internal就不用public
- 好处:类暴露成员太多,会让调用者痛苦,并且程序的安全性降低;又叫最少知识原则,一个对象应当对其他对象有尽可能少的了解,不和陌生人说话
- 具体做法
- 类的成员、类本身、尽量降低访问权限
- 类的内部成员尽量不要直接暴露,而是使用属性(只读等)
- 类的设计,尽量最小化
- 类之间的引用,尽量最少
二、继承的基本使用
- 继承的特点
- 继承顺序的不可逆性:继承是从上往下一次继承,而不可逆序进行
- 继承的延续性:在继承顺序中,下一级具备上一级的属性和特性
- 继承的必要性
- 代码复用,避免重复
- 一处更新,处处更新
- 继承的使用方法
- 抽象公共部分,放在公用类中(父类)
- 其他类(子类)只需要继承父类,即可拥有父类特征(属性和方法)
- 根据子类的需求添加属于自己的特征和方法
- 具体实现:通过冒号实现继承
- 父类和子类的概述
- 子类继承父类,父类派生子类
- 子类又叫派生类,父类叫基类或超类
- 子类继承父类成员,又有自己独立的成员
- 继承条件:继承需要符合的关系:is-a的关系
- 继承中的构造函数
- 使用this关键字访问父类成员
class Animal { public Animal(){} public Animal(string name,string color,string kind) { this.Color = color; this.Name = name; this.Kind = kind; } public string Name{get;set;} public string Color{get;set;} public string Kind{get;set;} public string Favorite{get;set;} } class Cat:Animal { //先初始化父类再初始化子类 public Cat(string name,string color,string kind):base(name,color,kind) { this.Favorite= favorite; } }
- 继承中的关键字:this,base,protected
- 隐式调用:子类的构造函数没有使用base只能调用父类哪个构造函数时,子类默认调用无参构造,此时父类需要提供无参构造
- 显示调用:如果父类没有无参构造,子类构造函数必须指明调用父类哪个构造函数
- 使用this关键字访问父类成员
- 继承的总结
- 传递性,A->B,B->C,C具有A的特性
- 单根性,单一继承
三、抽象类和抽象方法
- 概念与使用要点
- 使用关键字abstract修饰的类,称为抽象类
- 抽象类只是用来列举一个类所具有的的行为,不能单独通过创建对象来使用,如Animal animal = new Animal();是错误的
- 抽象类可以有抽象方法,也可以没有任何抽象方法
- 抽象类不能是静态的(static)或是密封的(sealed)
- 在抽象类中使用abstract修饰的方法称为抽象方法
- 抽象方法必须在抽象类中定义,不能再普通类中使用
- 抽象方法只是一个方法的声明,不能有任何方法体
- 抽象方法仅仅表示一个应该具有的行为,具体实现由其子类实现
- 抽象方法在子类中被实现(重写)必须使用关键字override
- 子类必须重写父类的所有方法,除非子类本身也是抽象类
class Animal { public string Name{get;set;} public string Color{get;set;} public string Kind{get;set;} public string Favorite{get;set;} public void Introduced() { string info = string.Format("我是{0},穿{1}的衣服,喜欢吃{2}}",Name,Color,Favorite); Console.WriteLine(info); } public abstract void Have(); } class Cat:Animal { public Cat(string name,string color,string kind,string favorite):base(name,color,kind) { this.favorite = favorite; } public void Dancing() { Console.WriteLine("....."); } //重写父类方法 public override Have() { Console.WriteLine("重写的具体业务逻辑"); } } abstract class OtherAnimal:Animal { public OtherAnimal(string name,string color,string kind,string favorite):base(name,color,kind) } static void Main(string[] args) { Cat objCat = new Cat("球球","黄色","小花猫","小鱼"); Dog objDog = new Cat("棒棒","黑色","小黑狗","排骨"); List<Animal> list = new List<Animal>(); list.Add(objCat); list.Add(objDog); foreach(Animal item in list) { item.Have(); } }
四、多态与里氏替换原则
- 多态理解
- 不同对象,接收相同消息,产生不同行为,称为多态
- 多态是由虚拟机(CLR)自行决定的
- 多态实现与应用
- 使用继承实现多态
- 父类中必须有抽象方法或虚方法
- 子类必须重写父类中的抽象方法或虚方法
- 子类对象必须转换成父类型去使用
- 多态提高了程序的可扩展性
- 使用继承实现多态
- 里氏替换原则
- 子类的对象能够替换父类
- 父类对象不能替换子类
- 父类方法都要在子类中实现或重写
五、is与as操作符
- is检查对象是否与指定类型兼容
- as用于在兼容的引用类型之间执行转换,转换失败返回null
六、虚方法的使用
- 使用场景:需要在父类中提供一个方法,该方法有自己的方法,子类根据自己的需要决定是否重写该方法,而不是必须重写
-
class Animal { public string Name{get;set;} public string Color{get;set;} public string Kind{get;set;} public string Favorite{get;set;} public void Introduced() { string info = string.Format("我是{0},穿{1}的衣服,喜欢吃{2}}",Name,Color,Favorite); Console.WriteLine(info); } public virtual void Have() { Console.WriteLine("Animal的Have方法"); } } class Cat:Animal { public Cat(string name,string color,string kind,string favorite):base(name,color,kind) { this.favorite = favorite; } public void Dancing() { Console.WriteLine("....."); } //重写父类方法,重写则调用子类方法,不重写则调用父类方法 public override Have() { Console.WriteLine("来自Cat的Have方法"); } } static void Main(string[] args) { Cat objCat = new Cat("球球","黄色","小花猫","小鱼"); Dog objDog = new Cat("棒棒","黑色","小黑狗","排骨"); List<Animal> list = new List<Animal>(); list.Add(objCat); list.Add(objDog); foreach(Animal item in list) { item.Have(); } }
虚方法 抽象方法 用virtual修饰 用abstract 要有方法体,哪怕只是一个分号 不允许使用方法体 可以被子类override 必须被子类override 除了密封类都可以写 只能在抽象类中 //重写Tostring方法 class Student { public int StuId{get;set;} public string StuName{get;set;} //Tostring方法为Object类的虚方法 public override string Tostring() { return string.Format("学号={0},姓名={1}",StuId,StuName); } } static void Main(string[] args) { int a = 100; Console.WriteLine(a.Tostring());//值类型已经被系统重写 }
七、密封类和方法覆盖
- 密封类
- 关键字sealed
- 当一个类被sealed修饰后,该类不能被继承
- sealed对于保护知识产权起到一定作用,一定程度限制了他人的二次开发
//该类不能作为父类被继承 sealed class NoveList { ........... }
- 关键字sealed
- 方法覆盖
- 使用new关键字覆盖父类中的同名方法
class Animal { public string Name{get;set;} public string Color{get;set;} public string Kind{get;set;} public string Favorite{get;set;} public void Introduced() { string info = string.Format("我是{0},穿{1}的衣服,喜欢吃{2}}",Name,Color,Favorite); Console.WriteLine(info); } public virtual void Have() { Console.WriteLine("Animal的Have方法"); } } class Dog:Animal { //使用new覆盖父类中的Introduced方法 public new void Introduced() { string info = string.Format("HI,My name is{0},穿{1}的衣服,喜欢吃{2}}",Name,Color,Favorite); Console.WriteLine(info); } } }
- 使用new关键字覆盖父类中的同名方法
八、简单总结
- 封装:隐藏内部实现,稳定外部接口->系统安全性
- 继承:子类继承父类成员,实现代码复用->开发和维护效率
- 多态:不同子类,对同一消息,做出不同反应->系统扩展性
- 实现1:父类作为方法参数,作为方法返回值
- 实现2:接口作为方法参数,接口作为返回值