zoukankan      html  css  js  c++  java
  • Java设计模式(二)设计模式原则

    学习Java设计模式之前,有必要先了解设计模式原则。

    开闭原则

    定义

    • 一个软件实体如类、模块和函数应该对扩展开放,对修改关闭

    • 用抽象构建框架,用实现扩展细节

    • 优点:提高软件系统的可复用性及可维护性

    Coding

    创建接口

    public interface ICourse {
        Integer getId();
    
        String getName();
    
        Double getPrice();
    }
    

    创建实现类

    @ToString
    @AllArgsConstructor
    public class JavaCourse implements ICourse {
    
        @Getter
        private Integer id;
    
        @Getter
        private String name;
    
        @Getter
        private Double price;
    }
    

    测试类

    public class Test {
    
        public static void main(String[] args) {
            ICourse iCourse = new JavaCourse(96, "我的Java课程", 348d);
            System.out.println("课程ID: " + iCourse.getId() + " 课程名称: " + iCourse.getName() + "课程价格: " + iCourse.getPrice());
        }
    }
    

    控制台输出

    课程ID: 96 课程名称: 我的Java课程课程价格: 348.0
    

    如果现在要打折出售课程,按照开闭原则来设计,对扩展开放,对修改关闭。

    创建打折类

    public class JavaDiscountCourse extends JavaCourse {
        public JavaDiscountCourse(Integer id, String name, Double price) {
            super(id, name, price);
        }
    
        public Double getOriginPrice() {
            return super.getPrice();
        }
    
        @Override
        public Double getPrice() {
            return super.getPrice() * 0.8;
        }
    }
    

    修改应用类

    public class Test {
    
        public static void main(String[] args) {
            ICourse javaCourse = new JavaDiscountCourse(96, "我的Java课程", 348d);
    
            JavaDiscountCourse iCourse = (JavaDiscountCourse) javaCourse;
            System.out.println("课程ID: " + iCourse.getId() + " 课程名称: " + iCourse.getName() + "课程原价: " + iCourse.getOriginPrice() + " 课程折后价格: " + iCourse.getPrice());
        }
    }
    

    控制台输出

    课程ID: 96 课程名称: 我的Java课程课程原价: 348.0 课程折后价格: 278.40000000000003
    

    这里有个要注意的地方,Double * 0.8后输出的浮点数精度有丢失的情况,可以使用BigDecimalString构造器public BigDecimal(String val)来解决。

    修改JavaDiscountCourse

    public class JavaDiscountCourse extends JavaCourse {
        public JavaDiscountCourse(Integer id, String name, Double price) {
            super(id, name, price);
        }
    
        public Double getOriginPrice() {
            return super.getPrice();
        }
    
        @Override
        public Double getPrice() {
            return new BigDecimal(super.getPrice().toString()).multiply(new BigDecimal("0.8")).doubleValue();
        }
    }
    

    控制台输出

    课程ID: 96 课程名称: 我的Java课程课程原价: 348.0 课程折后价格: 278.4
    

    依赖倒置原则

    定义

    • 高层模块不应该依赖低层模块,二者都应该依赖其抽象
    • 抽象不应该依赖细节;细节应该依赖抽象
    • 针对接口编程,不要针对实现编程
    • 优点:可以减少类间的耦合性、提高系统稳定性,提高代码可读性和可维护性,可降低修改程序所造成的风险

    Coding

    反例

    创建类

    public class Geely {
        public void studyJavaCourse() {
            System.out.println("Geely在学习Java课程");
        }
    
        public void studyFECourse() {
            System.out.println("Geely在学习FE课程");
        }
    
        public void studyPythonCourse() {
            System.out.println("Geely在学习Python课程");
        }
    }
    

    测试类

    public class Test {
        // v1
        public static void main(String[] args) {
            Geely geely = new Geely();
            geely.studyFECourse();
            geely.studyJavaCourse();
        }
    }
    

    控制台输出

    Geely在学习FE课程
    Geely在学习Java课程
    

    这时候,如果我们要让Geely学习Ruby课程,我们只能在Geely类中添加

    public void studyRubyCourse() {
        System.out.println("Geely在学习Ruby课程");
    }
    

    然后,在Test类中添加

    geely.studyRubyCourse();
    

    {% note warning %}

    不符合依赖倒置原则

    {% endnote %}

    正例

    创建接口

    public interface ICourse {
        void studyCourse();
    }
    

    创建类,带有成员变量ICourse course

    @AllArgsConstructor
    public class Geely {
    
        @Setter
        private ICourse course;
    
        public void studyImoocCourse() {
            course.studyCourse();
        }
    
    }
    

    创建实现类

    public class FECourse implements ICourse {
        @Override
        public void studyCourse() {
            System.out.println("Geely在学习FE课程");
        }
    }
    public class JavaCourse implements ICourse {
        @Override
        public void studyCourse() {
            System.out.println("Geely在学习Java课程");
        }
    }
    public class PythonCourse implements ICourse {
        @Override
        public void studyCourse() {
            System.out.println("Geely在学习Python课程");
        }
    }
    

    测试类

    public class Test {
        public static void main(String[] args) {
            Geely geely = new Geely(new JavaCourse());
            geely.studyImoocCourse();
    
            geely.setCourse(new FECourse());
            geely.studyImoocCourse();
        }
    }
    

    控制台输出

    Geely在学习Java课程
    Geely在学习FE课程
    

    这样一来,如果要添加新的课程,只需要创建实现类即可。然后应用类设置实现类,无需改动其他代码,符合依赖倒置原则。

    单一职责原则

    定义

    • 不要存在多于一个导致类变更的原因
    • 一个类/接口/方法只负责一项职责
    • 优点:降低类的复杂度、提高类的可读性、提高系统的可维护性、降低变更引起的风险

    Coding

    反例

    创建类

    public class Bird {
        public void mainMoveMode(String birdName) {
            System.out.println(birdName + " 用翅膀飞");
        }
    }
    

    测试类

    public class Test {
    
        public static void main(String[] args) {
            Bird bird = new Bird();
            bird.mainMoveMode("大雁");
            bird.mainMoveMode("鸵鸟");
        }
    }
    

    控制台输出

    大雁 用翅膀飞
    鸵鸟 用翅膀飞
    

    鸵鸟是用脚走的,所以我们更改Bird类

    public class Bird {
        public void mainMoveMode(String birdName) {
            if ("鸵鸟".equals(birdName)) {
                System.out.println(birdName + " 用脚走");
            } else {
                System.out.println(birdName + " 用翅膀飞");
            }
        }
    }
    

    如果有更多的鸟类,我们还要写更多的else代码。

    正例

    我们修改下反例中的例子

    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("鸵鸟");
        }
    }
    

    控制台输出

    大雁 用翅膀飞
    鸵鸟 用脚走
    

    再举一个例子

    创建接口

    /**
     * 课程内容
     *
     * @author gaochen
     * Created on 2019/7/27.
     */
    public interface ICourseContent {
    
        String getCoursName();
    
        byte[] getCourseVideo();
    
    }
    /**
     * 课程管理
     *
     * @author gaochen
     * Created on 2019/7/27.
     */
    public interface ICourseManager {
        void studyCourse();
    
        void refundCourse();
    }
    

    创建实现类,有着课程内容和课程管理两种职能

    public class CourseImpl implements ICourseContent, ICourseManager {
        @Override
        public String getCoursName() {
            return null;
        }
    
        @Override
        public byte[] getCourseVideo() {
            return new byte[0];
        }
    
        @Override
        public void studyCourse() {
    
        }
    
        @Override
        public void refundCourse() {
    
        }
    }
    

    接口隔离原则

    定义

    • 用多个专门的接口,而不使用单一的总接口,客户端不应该依赖它不需要的接口
    • 一个类对一个类的依赖应该建立在最小的接口上
    • 建立单一接口,不要建立庞大臃肿的接口
    • 尽量细化接口,接口中的方法尽量少
    • 优点:符合我们常说的高内聚低耦合的设计思想,从而使得类具有很好的可读性、可扩展性和可维护性。

    {% note warning %}

    注意适度原则,一定要适度

    {% endnote %}

    Coding

    反例

    创建接口

    public interface IAnimalAction {
    
        void eat();
    
        void fly();
    
        void swim();
    }
    

    创建实现类

    public class Bird implements IAnimalAction {
        @Override
        public void eat() {
            System.out.println("鸟 吃饭");
        }
    
        @Override
        public void fly() {
            System.out.println("鸟 飞");
        }
    
        @Override
        public void swim() {
            // 鸟不会游泳,空实现
        }
    }
    public class Dog implements IAnimalAction {
        @Override
        public void eat() {
            System.out.println("狗 吃饭");
        }
    
        @Override
        public void fly() {
            // 狗不会飞,空实现
        }
    
        @Override
        public void swim() {
            System.out.println("狗 游泳");
        }
    }
    

    我们可以看出,鸟和狗实现了接口后,各自都有无用的接口,所以违反了接口隔离原则,只能采取空实现的方式。但是对于使用方来说,还是可以调用狗的fly方法,得到空的实现。

    正例

    将反例中的接口接口拆分为三个独立的接口

    public interface IEatAnimalAction {
        void eat();
    }
    public interface IFlyAnimalAction {
        void fly();
    }
    public interface ISwimAnimalAction {
        void swim();
    }
    

    Dog改为

    public class Dog implements IEatAnimalAction,ISwimAnimalAction {
        @Override
        public void eat() {
            System.out.println("狗 吃饭");
        }
    
        @Override
        public void swim() {
            System.out.println("狗 游泳");
        }
    }
    

    Bird改为

    public class Bird implements IEatAnimalAction,IFlyAnimalAction {
        @Override
        public void eat() {
            System.out.println("鸟 吃饭");
        }
    
        @Override
        public void fly() {
            System.out.println("鸟 飞");
        }
    }
    

    这样就成功的将一个大接口,优化为分摊职责的小接口,实现类可以根据需要实现多个职能接口。

    迪米特原则

    定义

    • 一个对象应该对其他对象保持最少的了解。又叫最少知道原则
    • 尽量降低类与类之间的耦合
    • 优点:降低类之间的耦合

    Coding

    反例

    创建课程类

    public class Course {
    }
    

    创建项目经理类

    public class TeamLeader {
    
        public void checkNumberOfCourse(List<Course> courseList) {
            System.out.println("在线课程的数量是 :" + courseList.size());
        }
    }
    

    创建老板类

    public class Boss {
    
        public void commandCheckNumber(TeamLeader teamLeader) {
            List<Course> courseList = new ArrayList<>();
            for (int i = 0; i < 20; i++) {
                courseList.add(new Course());
            }
            teamLeader.checkNumberOfCourse(courseList);
        }
    }
    

    测试类

    在线课程的数量是 :20
    

    我们仔细分析一下,其实老板并不需要知道课程的细节,只需要问一下项目经理,有多少课程,项目经理直接告诉老板有20节在线课程。而不是老板将课程列出,让项目经理统计。

    我们看下UML类图

    正例

    项目经理类修改为

    public class TeamLeader {
        
        public void checkNumberOfCourse() {
            List<Course> courseList = new ArrayList<>();
            for (int i = 0; i < 20; i++) {
                courseList.add(new Course());
            }
            System.out.println("在线课程的数量是 :" + courseList.size());
        }
    }
    

    老板类

    public class Boss {
    
        public void commandCheckNumber(TeamLeader teamLeader) {
            teamLeader.checkNumberOfCourse();
        }
    }
    

    这时候运行一下,结果一样。但是从UML类图上来看,是有很大的优化的。

  • 相关阅读:
    Java_JAVA6动态编译的问题
    Java_动态加载类(英文)
    Java_Java Compiler 应用实例
    Java_关于App class loader的总结
    Java_动态加载
    Java_Java SE6调用动态编译
    python捕获Ctrl+C信号
    python使用协程并发
    python使用多进程
    python使用多线程
  • 原文地址:https://www.cnblogs.com/gcdd/p/12292118.html
Copyright © 2011-2022 走看看