我是清风~,每天学习一点点,快乐成长多一点,这些都是我的日常笔记以及总结。
开闭原则
- 定义:一个软件实体如类、模块和函数应该对扩展开发,对修改关闭 用抽象构建框架,用实现拓展细节
- 优点:提高软件系统的可复用性及可维护性
- 核心思想:面向抽象编程,而不是面向具体的实现编程,因为抽象相对来说是稳定的,让类去依赖于固定的抽象,所有对修改来说说就是封闭的。通过面向对象的继承和多态的机制,可以实现对抽象体的继承,通过重写改变其固有方法,或者实现新的扩展方法。当变化发生时,可以通过创建抽象来隔离以后有可能发生的同类变化
下面实现代码,我想要一个女神,想要她符合我几个要求,胸大,腰好,萝莉范
//创建一个接口,三个变量,你心里目中的女神属性
public interface Girlfriend {
Integer bust(); //胸
String figure();//身材
String type(); //哪种气质女生
}
创建女神的接口实现类
public class Goddess implements Girlfriend {
private Integer bust;
private String figure;
private String type;
public Goddess(Integer bust, String figure, String type) {
this.bust = bust;
this.figure = figure;
this.type = type;
}
@Override
public Integer bust() {return this.bust; }
@Override
public String figure() {return this.figure;}
@Override
public String type() {return this.type; }
}
假设我们要给我们的女神增加胸围,这样符合我们的要求 ,
我不需要在接口里面添加,可以重写父类方法,原来的方法属性也不会丢失
public class GoddessType extends Goddess {
public GoddessType(Integer bust, String figure, String type) {
super(bust, figure, type);
}
//我还要原来的胸围做对比
public Integer Orginbust(){
return super.bust();
}
//新增女神的特点,我想要女神是胸围更大一点,36d的
//我在接口里面定义新的方法,那么女神实现类就要发生更改
//但是这就会发生臃肿,你的女神整体都要更改。
//接口,应该是稳定可靠的,不应该经常变动的
//重写父类的bust方法
@Override
public Integer bust() {//越来越好
return super.bust()+12;
}
}
然后我们new一个我们想要的女神出来
public class MyDayDream {
public static void main(String[] args) {
Girlfriend girlfriend = new GoddessType(24,"水蛇腰","小萝莉");
GoddessType goddess = (GoddessType) girlfriend;
System.out.println("你做梦的女神:她是一个有着" + goddess.bust() + "d令人垂涎"+
goddess.figure() +"身材,人见人爱的"+ goddess.type() + ",虽然胸围只有"
+ goddess.Orginbust()+"d");
}
}
输出
你做梦的女神:她是一个有着36d令人垂涎水蛇腰身材,人见人爱的小萝莉,虽然胸围只有24d
虚线是接口,实现是继承,子类构造器GoddessType调用父类的构造器
依赖倒置原则
- 定义:高层模块不应该依赖底层模块,二者都应该依赖其抽象
- 抽象不应该依赖细节;细节应该依赖抽象
- 针对接口编程,不要针对实现编程。使模块类之间彼此独立互不影响,从而实现模块之间的松耦合,降低模块之间的耦合性
实现思路:
一个小明在CSDN上学习,可能对java课程感兴趣,对python也感兴趣,突然某一天帅哥想去学习大数据,那么实体课程就需要不断补充,你要是面向实现编程,因为小明这个类就是实现类,一直要扩展,扩展性很差。
应用层的函数,是依赖于底层实现的,要是没有抽象,就会依赖于小明这个类,现在属于高层次模块依赖于低层次模块。应用层,实现什么都要去帅哥类里面去扩展,这是不可取的。
具体代码,下面引入抽象,
首先创建接口,具体学习什么课程,交给高层模块的应用层来选择
public interface ICourse {
void studyCourse();
}
来实现接口,这个课程类
创建三种不同课程学习
java课程学习、python课程学习、data课程学习
public class JavaCourse implements ICourse {
@Override
public void studyCourse() {
System.out.println("XiaoMing在学习Java课程");
}
}
public class PythonCourse implements ICourse {
@Override
public void studyCourse() {
System.out.println("XiaoMing在学习Python课程");
}
}
public class DataCourse implements ICourse {
@Override
public void studyCourse() {
System.out.println("Xiaoing在学习大数据课程");
}
}
这里就是xiaoming在学校CSDN课程的时候,具体的实现类交给高层次模块
而不是针对xiaoming这个实现类来编写
public class XiaoMing {
public void studyCSDNCourse(ICourse iCourse){
iCourse.studyCourse();
}
}
public class Test {
public static void main(String[] args) {
XiaoMing xiaoming = new XiaoMing();
xiaoming.studyCSDNCourse(new JavaCourse());
xiaoming.studyCSDNCourse(new DataCourse());
xiaoming.studyCSDNCourse(new PythonCourse());
}
}
输出
XiaoMing在学习Java课程
XiaoMing在学习大数据课程
XiaoMing在学习Python课程
首先ICourse这个接口放这边,要是其他的课程实现,前端课程实现,和下面的java,python课程平级,而具体的xiaoming这个类是不需要动的,我们要是面向接口编程,我们要写的扩展类是面向接口的,而不是面向具体的xiaoming实现类,而高层模块,具体学习什么交给test来选择,这样就做到了xiaoming和test之间是解耦的,同时xiaoming和具体的课程实现是解耦的,但是xiaoming是跟ICourse是有耦合的
所谓的高内聚低耦合,就是尽量减少耦合
单一职责原则
- 定义:不要存在多于一个导致类变更的原因。假设有一个类class,负责两个职责,职责1和职责2,一旦需求变更,职责相关的功能需要发生改变,那么修改这个class类的时候,有可能会导致原本运行正常的职责2方式故障,这既是创建class没有遵循单一职责原则
- 体现在一个类、接口、方法、只负责一项职责
- 优点:降低类的复杂度、提高类的可读性,提高系统的可维护性,降低变更引起的风险。 这要是模块的数据都可以使用单一职责原则
设计结构:老鹰和老母鸡属于鸟,老鹰是用翅膀飞的,老母鸡是用脚走的
代码实现
public class Bird {
public void mainMoveMode(String birdName){
if("老母鸡".equals(birdName)){
System.out.println(birdName+"用脚走");
}else{
System.out.println(birdName+"用翅膀飞");
}
}
}
public class FlyBird {
public void mainMoveMode(String birdName){
System.out.println(birdName+"用翅膀飞");
}
}
public class WalkBird {
public void mainMoveMode(String birdName){
System.out.println(birdName+"用脚走");
}
}
public class Test {
public static void main(String[] args) {
FlyBird flyBird = new FlyBird();
flyBird.mainMoveMode("老鹰");
WalkBird walkBird = new WalkBird();
walkBird.mainMoveMode("老母鸡");
}
}
输出
老鹰用翅膀飞
老母鸡用脚走
接口隔离原则
- 定义:用多个专门的接口,而不适用单一的总接口,客户端不应该依赖它不需要的接口
- 一个类对一个类的依赖应该建立在最小的接口上
- 建立单一接口,不要建立庞大臃肿的接口
- 尽量细化接口,接口中的方法尽量少
代码
会吃、会飞、会游泳的动物的接口
public interface IAnimalAction {
void eat();
void fly();
void swim();
}
只会吃的动物接口
public interface IEatAnimalAction {
void eat();
}
只会飞的动物接口
public interface IFlyAnimalAction {
void fly();
}
只会游泳的动物接口
public interface ISwimAnimalAction {
void swim();
}
鸟会吃、会飞、会游泳
public class Bird implements IAnimalAction {
@Override
public void eat() {
}
@Override
public void fly() {
}
@Override
public void swim() {
}
}
狗就只会吃、和游泳,不会飞,不需要实现飞的接口
public class Dog implements ISwimAnimalAction,IEatAnimalAction {
@Override
public void eat() {
}
@Override
public void swim() {
}
}
对接口进行隔离,因为细力度可以组装的,组力度是不可以拆分的
- 单一职责原则指的是类,接口、方法,职责是单一的,强调是职责,只要是职责是单一的,多个方法也可以
- 接口隔离原则注重是对接口依赖的隔离
- 单一职责原则约束的是类,接口,方法,针对的是程序的实现和细节
- 接口隔离原则主要约束的就是,针对抽象,针对整体框架的构建,实际中,接口小没有问题,但是接口数量过多不好
迪米特原则
-
定义:一个对象应该对其他保持最少的了解。又叫最少知道原则
-
尽量降低类与类之间的耦合
-
如果一个方法放在本类中 既不增加类间关系,也不产生负面影响,就可以放到本类中
-
强调只和朋友交流,不和陌生人说话
-
朋友:出现在成员变量、方法的输入、输出参数中的类称为成员朋友类,而出现的方法内部的类不属于朋友类。
代码实现
比如CSDN有一个大老板,让teamleader查下,线上有多少个课程,这里就关系到几个类,boss,teamleader,course类
boss给teamleader下指令,不需要知道course的情况,只需要teamleader把查完的结果告诉boss就可以
boss和teamleader有直接关系,不应该跟陌生的course有关系
public class Boss {
//查课程的数量
public void commandCheckNumber(TeamLeader teamLeader){
teamLeader.checkNumberOfCourses();
}
}
public class TeamLeader {
public void checkNumberOfCourses(){
List<Course> courseList = new ArrayList<Course>();
for(int i = 0 ;i < 20;i++){
courseList.add(new Course());
}
System.out.println("在线课程的数量是:"+courseList.size());
}
}
public class Course {
}
public class Test {
public static void main(String[] args) {
Boss boss = new Boss();
TeamLeader teamLeader = new TeamLeader();
boss.commandCheckNumber(teamLeader);
}
}
UML看出,boss不需要知道courser类,course是由teamleader来生成的
里氏替换原则
- 定义:对每一个类型T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有的对象o1都替换成o2时,程序p的行为没有发生变化,那么类型T2是类型T1的子类型。
- 引申出来,继承,T2是T1的子类型,T1是T2的父类,里氏替换原则,是继承父类的基石,只有当子类可以替换父类,并且软件功能不受影响时,父类才能真正的被复用,而子类也能在父类的基础上增加新的行为
- 里氏替换原则是对开闭原则的一个补充,实现开闭原则的关键就是抽象,父类和子类的关系就是抽象化的具体实现
- 所以里氏替换原则非常重要,他是对实现抽象化的具体步骤规范化的,注意规范他来约束我们 里氏替换原则就是反对子类重写父类方法的这一层含义
引申意义:子类可以扩展父类的功能,但不能改变父类原有功能
- 含义1:子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法
- 含义2:子类中可以增加自己持有的方法。
- 含义3:当子类的方法重载父类方法时,方法的前置条件(即方法的输入/入参)要比父类方法的输入参数更宽松。
- 含义4:当子类的方法实现父类的方法时(重写/重载或实现抽象方法),方法的后置条件(即方法的输出/返回值)要比父类更严格或相等。
优点:
- 约束继承泛滥,开闭原则的一种体现
- 加强程序的健壮性,同时变更也可以做到非常好的兼容性,提高程序的维护性、扩展性。降低需求变更时引入的风险
设计思路:
长方形和正方形,正方形是一种特殊的长方形,建立一个父类,正方形来继承这个长方形父类
代码
长方形接口
public interface Quadrangle {
long getWidth();
long getLength();
}
public class Rectangle implements Quadrangle {
private long length;
private long width;
@Override
public long getWidth() {
return width;
}
@Override
public long getLength() {
return length;
}
public void setLength(long length) {
this.length = length;
}
public void setWidth(long width) {
this.width = width;
}
}
public class Square implements Quadrangle {
private long sideLength;
public long getSideLength() {
return sideLength;
}
public void setSideLength(long sideLength) {
this.sideLength = sideLength;
}
@Override
public long getWidth() {
return sideLength;
}
@Override
public long getLength() {
return sideLength;
}
}
如果传四边形对象Quadrangle,set是不行的,因为这个四边形接口并没有声明set长和宽
这个就是我们的父类并没有赋值方法,
因此整个resize方法是不可能适用于四边形类型的,这个方法就给我提高了约束,我们想传子类进来,发现不可以,就不能传了
这个resize只能适用它的子类,它的长方形是可以的,这样就禁止继承泛滥
public class Test {
public static void resize(Rectangle rectangle){
while (rectangle.getWidth() <= rectangle.getLength()){
rectangle.setWidth(rectangle.getWidth()+1);
System.out.println("width:"+rectangle.getWidth() + "
length:"+rectangle.getLength());
}
System.out.println("resize方法结束 width:"+rectangle.getWidth() + "
length:"+rectangle.getLength());
}
public static void main(String[] args) {
Square square = new Square();
}
}
UML类图
代码2
public class Base {
public void method(HashMap map){
System.out.println("父类被执行");
}
}
public class Child extends Base {
//如果此方法不注释,会现调用这个方法
// @Override
// public void method(HashMap map) {
// System.out.println("子类HashMap入参方法被执行");
// }
public void method(Map map) {
System.out.println("子类HashMap入参方法被执行");
}
}
public class Test {
public static void main(String[] args) {
Base child = new Child();
HashMap hashMap = new HashMap();
child.method(hashMap);
}
}
输出
父类被执行
合成(组合)/聚合复用原则
- 定义:尽量使用对象组合/聚合,而不是继承关系达到软件复用的目的
- 聚合has-A和组合contains-A
- 优点:可以使系统更加灵活,降低类与类之间的耦合度,一个类的变化对其他类造成的影响相对较少。
代码
创建数据库的连接,获取什么样的连接,一个使mysql和PostgreSQL
public abstract class DBConnection {
public abstract String getConnection();
}
public class MySQLConnection extends DBConnection {
@Override
public String getConnection() {
return "MySQL数据库连接";
}
}
public class PostgreSQLConnection extends DBConnection {
@Override
public String getConnection() {
return "PostgreSQL数据库连接";
}
}
重点使productDao了,这里不需要继承了,直接通过组合方式把连接注入进来
public class ProductDao{
private DBConnection dbConnection;
public void setDbConnection(DBConnection dbConnection) {
this.dbConnection = dbConnection;
}
public void addProduct(){
String conn = dbConnection.getConnection();
System.out.println("使用"+conn+"增加产品");
}
}
public class Test {
public static void main(String[] args) {
ProductDao productDao = new ProductDao();
productDao.setDbConnection(new PostgreSQLConnection());
productDao.addProduct();
}
}
输出
使用PostgreSQL数据库连接增加产品
这就是组合复用原则,去掉了productdao与DBconnection之间联系,他们之间使一对一的组合关系
PostgreSQLConnection和MySQLConnection继承DBconnection