学习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设计模式(二)设计模式原则的更多相关文章

  1. Java 设计模式(二)-六大原则

    Java 设计模式(二)-六大原则 单一职责原则(Single Responsibility Principle) 定义: 不要存在多余一个原因导致类变更,既一个类只负责一项职责. 问题由来: 当类A ...

  2. 图解Java设计模式之设计模式七大原则

    图解Java设计模式之设计模式七大原则 2.1 设计模式的目的 2.2 设计模式七大原则 2.3 单一职责原则 2.3.1 基本介绍 2.3.2 应用实例 2.4 接口隔离原则(Interface S ...

  3. java设计模式之七大原则

    java设计模式 以下内容为本人的学习笔记,如需要转载,请声明原文链接   https://www.cnblogs.com/lyh1024/p/16724932.html 设计模式 1.设计模式的目的 ...

  4. java 23 种设计模式

    一.设计模式的分类 总体来说设计模式分为三大类: 创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. 结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥接 ...

  5. (转)java 23种设计模式

    设计模式(Design Patterns) ——可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了 ...

  6. C#之设计模式之六大原则(转载)

    设计模式之六大原则(转载) 关于设计模式的六大设计原则的资料网上很多,但是很多地方解释地都太过于笼统化,我也找了很多资料来看,发现CSDN上有几篇关于设计模式的六大原则讲述的比较通俗易懂,因此转载过来 ...

  7. Java进阶篇 设计模式之十四 ----- 总结篇

    前言 本篇是讲述之前学习设计模式的一个总结篇,其目的是为了对这些设计模式的进行一个提炼总结,能够通过查看看此篇就可以理解一些设计模式的核心思想. 设计模式简介 什么是设计模式 设计模式是一套被反复使用 ...

  8. Java进阶篇设计模式之一 ----- 单例模式

    前言 在刚学编程没多久就听说过设计模式的大名,不过由于当时还是个彻彻底底的菜鸟,并没有去触碰.直到在开始工作中对简单的业务代码较为熟悉之后,才正式的接触设计模式.当时最早接触的设计模式是工厂模式,不过 ...

  9. 实践GoF的23种设计模式:SOLID原则(上)

    摘要:本文以我们日常开发中经常碰到的一些技术/问题/场景作为切入点,示范如何运用设计模式来完成相关的实现. 本文分享自华为云社区<实践GoF的23种设计模式:SOLID原则(上)>,作者: ...

  10. Java中单态设计模式

    Java中单态设计模式 2011-09-23 16:38:46|  分类: Java |  标签:technology!  |举报|字号 订阅     此博文是转自新浪博客中一名叫做"俊俊的 ...

随机推荐

  1. Python3(二) 表示‘组’的概念与定义

    现实世界中总存在一组一组的事物, 一.列表的定义 type(['hello','world',1,9,True,False]) = <class 'list'> type([[1,2,3, ...

  2. 动态获取bind dns日志IP脚本

    #!/usr/bin/env python #_*_coding:utf-8_*_ ''' python deny_dns_allip.py your_filelog_name 动态获取dns日志的I ...

  3. SSM/SSH框架的MySQL 读写分离实现的一种简单方法

    简介 MySQL已经是使用最为广泛的一种数据库,往往实际使用过程中,为实现高可用及高性能,项目会采用主丛复制的方式实现读写分离.MySQL本身支持复制,通过简单的配置即可实现一主多从的配置,具体实现可 ...

  4. [PowerShell]Windows服务开启、重启、关闭

    # 获取服务信息 PS C:\Users\Administrator> Get-Service win* Status Name DisplayName ------ ---- -------- ...

  5. AOP in .NET

    AOP in .NET AOP是所有现代OOP语言开发框架中的基础功能,随着Spring框架的普及,对于AOP的使用已经像喝水一样普通.可是知其然还要其所以然.本文将基于.NET环境探讨实现AOP的底 ...

  6. asp.net core 3.x Identity

    一.前言 这方面的资料很多,重复的写没必要,但是最近一直在学习身份验证和授权相关东东,为了成体系还是写一篇,主要是从概念上理解identity系统. 参考:https://www.cnblogs.co ...

  7. C#设计模式学习笔记:(9)组合模式

    本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/7743118.html,记录一下学习过程以备后续查用. 一.引言 今天我们要讲结构型设计模式的第四个模式--组 ...

  8. 避免XSS攻击

    遭遇XSS攻击怎么解决 XSS的攻击手段 利用JavaScript或DOM方式进行攻击,XSS(脚本注入)提交,然后进行页面展示,影响页面的正常结构,还可以做钓鱼网站,来盗取用户的信息. 比如在页面评 ...

  9. net start MySQL57 MySQL57 服务正在启动 . MySQL57 服务无法启动。

    造成这种情况的原因有很多,如果直接百度错误信息的话,不一定能很快解决问题,所以,出现这种情况,我们可以使用 mysqld --console 命令来查看报错信息,然后根据报错信息来百度,这样就很快定位 ...

  10. unity 教程Tanks中的Transform.InverseTransformPoint理解

    Tanks教程中在处理摄像机缩放的时候使用了下面的函数,取两个坦克的中心点之后,根据两个坦克之间的距离,保证两个坦克都在屏幕中,然后进行缩放. private float FindRequiredSi ...