【java设计模式】这么详细,你还不懂软件设计七大原则!

我是清风~,每天学习一点点,快乐成长多一点,这些都是我的日常笔记以及总结。

开闭原则

  • 定义:一个软件实体如类、模块和函数应该对扩展开发,对修改关闭 用抽象构建框架,用实现拓展细节
  • 优点:提高软件系统的可复用性及可维护性
  • 核心思想:面向抽象编程,而不是面向具体的实现编程,因为抽象相对来说是稳定的,让类去依赖于固定的抽象,所有对修改来说说就是封闭的。通过面向对象的继承和多态的机制,可以实现对抽象体的继承,通过重写改变其固有方法,或者实现新的扩展方法。当变化发生时,可以通过创建抽象来隔离以后有可能发生的同类变化

下面实现代码,我想要一个女神,想要她符合我几个要求,胸大,腰好,萝莉范

//创建一个接口,三个变量,你心里目中的女神属性

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来生成的
在这里插入图片描述

里氏替换原则

  1. 定义:对每一个类型T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有的对象o1都替换成o2时,程序p的行为没有发生变化,那么类型T2是类型T1的子类型。
  2. 引申出来,继承,T2是T1的子类型,T1是T2的父类,里氏替换原则,是继承父类的基石只有当子类可以替换父类,并且软件功能不受影响时,父类才能真正的被复用,而子类也能在父类的基础上增加新的行为
  3. 里氏替换原则是对开闭原则的一个补充,实现开闭原则的关键就是抽象,父类和子类的关系就是抽象化的具体实现
  4. 所以里氏替换原则非常重要他是对实现抽象化的具体步骤规范化的,注意规范他来约束我们 里氏替换原则就是反对子类重写父类方法的这一层含义

引申意义:子类可以扩展父类的功能,但不能改变父类原有功能

  • 含义1:子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法
  • 含义2:子类中可以增加自己持有的方法。
  • 含义3:当子类的方法重载父类方法时,方法的前置条件(即方法的输入/入参)要比父类方法的输入参数更宽松。
  • 含义4:当子类的方法实现父类的方法时(重写/重载或实现抽象方法),方法的后置条件(即方法的输出/返回值)要比父类更严格或相等。

优点:

  1. 约束继承泛滥,开闭原则的一种体现
  2. 加强程序的健壮性,同时变更也可以做到非常好的兼容性,提高程序的维护性、扩展性。降低需求变更时引入的风险

设计思路
长方形和正方形,正方形是一种特殊的长方形,建立一个父类,正方形来继承这个长方形父类

代码

长方形接口
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
在这里插入图片描述

发布了17 篇原创文章 · 获赞 18 · 访问量 3828

猜你喜欢

转载自blog.csdn.net/weixin_43464303/article/details/105611558