一.开饭封闭原则(Open-Closed Principle,OCP)
1.定义:
软件实体(包括类、模块、函数)应该为扩展而打开,为修改而关闭。
大概意思就是:一个软件实体应该通过扩展来实现变化,而不是通过修改原有的代码来实现变化
2.实现:
开闭原则实现关键是抽象
抽象基类:把系统的所有可能的行为抽象成一个抽象底层,这个抽象底层规定出所有的具体实现必须提供的方法的特征。作为系统设计的抽象层,要预见所有可能的扩展,从而使得在任何扩展情况下,系统的抽象底层不需修改
派生类:从抽象基类派生一个或多个新的具体实现,可以扩展基类的行为,系统设计对扩展开放
3.使用:
通过接口或者抽象类约束扩展,对于扩展进行边界限定
参数类型、引用对象尽量使用接口或者抽象方法,而不是实现类
抽象层应该保持稳定和可靠,一但确认即不允许修改
4.优点:
可复用、可维护
5.抽象方法示例:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace OCP.Study.Abstract
{
/// <summary>
/// 抽象类
/// 形状
/// </summary>
public abstract class Shape
{
protected string Name;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="name">形状名称</param>
public Shape(string name)
{
this.Name = name;
}
/// <summary>
/// 面积
/// </summary>
/// <returns></returns>
public abstract double Area();
/// <summary>
/// 显示
/// </summary>
public abstract void Display();
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace OCP.Study.Abstract
{
/// <summary>
/// 圆形
/// </summary>
public class Circular:Shape
{
private double Radius;
public Circular(string name,double radius) : base(name)
{
this.Radius = radius;
}
public override double Area()
{
return Math.Round(Math.PI*Radius*Radius,2);
}
public override void Display()
{
Console.WriteLine($"{Name}的面积是{this.Area()}");
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace OCP.Study.Abstract
{
/// <summary>
/// 矩形
/// </summary>
public class Rectangle : Shape
{
private double Width;//宽度
private double Length;//长度
public Rectangle(string name, double width, double length) : base(name)
{
this.Width = width;
this.Length = length;
}
public override double Area()
{
return Length * Width;
}
public override void Display()
{
Console.WriteLine($"{Name}的面积是{this.Area()}");
}
}
}
using OCP.Study.Abstract;
using OCP.Study.Interface;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace OCP.Study
{
class Program
{
static void Main(string[] args)
{
try
{
{
Shape Circular = new Circular("圆形", 2);
Circular.Area();
Circular.Display();
}
{
Shape Rectangle = new Rectangle("矩形", 7, 12);
Rectangle.Area();
Rectangle.Display();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadKey();
}
}
}
6.接口示例:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace OCP.Study.Interface
{
/// <summary>
/// 课程接口
/// </summary>
public interface ICourse
{
/// <summary>
/// 获取课程ID
/// </summary>
/// <returns></returns>
int GetId();
/// <summary>
/// 获取课程名称
/// </summary>
/// <returns></returns>
string GetName();
/// <summary>
/// 获取课程价格
/// </summary>
/// <returns></returns>
double GetPrice();
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace OCP.Study.Interface
{
/// <summary>
/// 课程C#
/// </summary>
public class CSharpCourse : ICourse
{
private int Id;
private string Name;
private double Price;
/// <summary>
/// 初始化
/// </summary>
/// <param name="id">编号</param>
/// <param name="name">名称</param>
/// <param name="price">价格</param>
public CSharpCourse(int id,string name,double price)
{
this.Id = id;
this.Name = name;
this.Price = price;
}
public int GetId()
{
Console.WriteLine(this.Id);
return this.Id;
}
public string GetName()
{
Console.WriteLine(this.Name);
return this.Name;
}
public double GetPrice()
{
Console.WriteLine(this.Price);
return this.Price;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace OCP.Study.Interface
{
public class CSharpCourseDisCount : CSharpCourse
{
private double DisCount;
public CSharpCourseDisCount(int id, string name, double price) : base(id, name, price)
{
this.DisCount = price*0.8;
}
public double GetDisCount()
{
Console.WriteLine(this.DisCount);
return DisCount;
}
}
}
using OCP.Study.Abstract;
using OCP.Study.Interface;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace OCP.Study
{
/// <summary>
/// 需求在原有的基础上添加折扣方法
/// </summary>
class Program
{
static void Main(string[] args)
{
try
{
ICourse CSharp = new CSharpCourseDisCount(1,"C#",0.9);
CSharpCourseDisCount CSharpCourse = (CSharpCourseDisCount)CSharp;
CSharpCourse.GetId();
CSharpCourse.GetName();
CSharpCourse.GetPrice();
CSharpCourse.GetDisCount();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadKey();
}
}
}
7.简单说一下抽象类和接口的区别
抽象类:
不能实例化
可以包含抽象方法和抽象访问器(一但包含了抽象方法就必须声明成抽象类)
不能用sealed修饰符(阻止其他类继承自该类)修饰抽象类,因为两个修饰符的含义相反,sealed
修饰符阻止类被继承,而 abstract
修饰符要求类被继承
从抽象类派生的非抽象类必须包括继承的所有抽象方法和抽象访问器的实际实现
接口:
不能被实例化
只能包含方法声明
接口的成员包括方法、属性、索引器、事件(不能包含常量、字段、静态成员、构造函数、析构函数)
相同点:
都不能被实例化
都可以被继承
都可以包含方法声明
派生类必须实现为实现的方法
区别:
抽象类中可以包含静态方法、构造方法、普通成员变量,而接口没有
一个类可以继承多个接口、但只能继承一个抽象类
接口可以被多重实现、抽象类只能被单一继承
如果抽象类实现接口,则可以把接口中方法映射到抽象类中作为抽象方法而不必实现,而在抽象类的子类中实现接口中方法
8.属性和字段的区别
属性是对字段的封装,在字段上添加了get、set两个方法
属性从实质上就是一种方法,而字段是用来存数据的