一、设计原则
1.开闭原则
定义:对扩展开放,对修改关闭
核心:面向抽象编程
业务描述:有一门JAVA课程(id、name、price),课程价格打折了。
public interface ICourse {
Integer getId();
String getName();
Double getPrice();
//添加
Double getDiscountPrice();
}
public class JavaCourse implements ICourse {
private int id;
private String name;
private double price;
public JavaCourse(int id, String name, double price) {
this.id = id;
this.name = name;
this.price = price;
}
@Override
public Integer getId() {
return this.id;
}
@Override
public String getName() {
return this.name;
}
@Override
public Double getPrice() {
return this.price;
}
//添加
@Override
public Double getDiscountPrice() {
return this.price * 0.8;
}
}
public class Test {
public static void main(String[] args) {
ICourse javaCourse = new JavaCourse(1, "java课程", 266d);
System.out.println(javaCourse.getPrice());//输出的就是266。
//业务开始
System.out.println(javaCourse.getDiscountPrice());//输出的就是打完折的价钱。
}
}
/**
** 这时对原代码作修改了,万一还有更多的业务,就需要修改大量的原代码,开闭原则的定义就是对修改关闭,对 ** 扩展开放,我们可以通过下面方式进行修改,即满足开闭原则。
**/
引入开闭原则
public interface ICourse {
Integer getId();
String getName();
Double getPrice();
// Double getDiscountPrice();
}
public class JavaCourse implements ICourse {
private int id;
private String name;
private double price;
public JavaCourse(int id, String name, double price) {
this.id = id;
this.name = name;
this.price = price;
}
@Override
public Integer getId() {
return this.id;
}
@Override
public String getName() {
return this.name;
}
@Override
public Double getPrice() {
return this.price;
}
/*@Override
public Double getDiscountPrice() {
return this.price * 0.8;
}*/
}
//增加一个类,继承JavaCourse类
public class JavaDiscountCourse extends JavaCourse {
//因为JavaCourse没有实现无参构造方法,所以必须实现带参构造方法
public JavaDiscountCourse(int 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 JavaCourse(1, "JAVA设计模式", 345d);
System.out.println(javaCourse.getPrice());
System.out.println(javaCourse.getDiscountPrice());*/
ICourse javaDiscountCourse = new JavaDiscountCourse(1, "JAVA设计模式", 345d);
System.out.println(((JavaDiscountCourse)
javaDiscountCourse).getOriginPrice());//获取原价
System.out.println(javaDiscountCourse.getPrice());//获取打折后的价格
}
}
//不需要改动原来的代码
UML关系图
1559719478255.png2.依赖倒置原则
定义:高层模块不应该依赖低层模块,二者都应该依赖其抽象
核心:面向接口编程
业务描述:我学习了Java、前端
public class My {
public void studyJava(){
System.out.println("我学习了Java");
}
public void studyPE(){
System.out.println("我学习了前端");
}
}
public class Test {
public static void main(String[] args) {
My me = new My();
me.studyJava();
me.studyPE();
}
}
我又学了python、oracle
public class My {
public void studyJava(){
System.out.println("我学习了Java");
}
public void studyPE(){
System.out.println("我学习了前端");
}
public void studyPython(){
System.out.println("我学习了python");
}
public void studyOracle(){
System.out.println("我学习了Oracle");
}
}
public class Test {
public static void main(String[] args) {
My me = new My();
me.studyJava();
me.studyPE();
me.studyPython();
me.studyOracle();
}
}
/**
** 会发现每当学习了一门课程,就得在My类上加一门课程,这就是面向实现编程,依赖实现。Test相对来说是高层,My是低层,依赖倒置原则是高层实现不依赖低层实现,而这完全就是Test的实现必须依赖My的方法实现。
加入抽象
public interface ICourse {
void studyCourse();
}
public class JavaCourse implements ICourse {
@Override
public void studyCourse() {
System.out.println("学习了Java");
}
}
public class PECourse implements ICourse{
@Override
public void studyCourse() {
System.out.println("学习了前端");
}
}
public class My {
private ICourse course;
public void studyCourse(){
course.studyCourse();
}
public void setCourse(ICourse course) {
this.course = course;
}
}
public class Test {
/*public static void main(String[] args) {
My me = new My();
me.studyJava();
me.studyPE();
me.studyPython();
me.studyOracle();
}*/
public static void main(String[] args) {
My me = new My();
me.setCourse(new JavaCourse());
me.studyCourse();
me.setCourse(new PECourse());
me.studyCourse();
}
}
/**
** 这时我们如果需要学习一门课程,只是新建一个课程类实现ICourse接口,在高层Test中指定My类学习哪门课程** 就行。这样做高层My不需要改动任何代码,任何课程间也是独立的。这就是面向接口的优点。
UML图
1559721506453.png3.单一职责原则
定义:不要存在多余一个导致类变更的原因
核心:一个类/接口/方法只负责一项职责
类的单一原则
public class Bird {
public void birdFly(String name){
System.out.println(name + "用翅膀飞");
}
}
public class Test {
public static void main(String[] args) {
Bird bird = new Bird();
bird.birdFly("大雁");
}
}
/**
** 结果输出大雁用翅膀飞
**/
public class Test {
public static void main(String[] args) {
Bird bird = new Bird();
bird.birdFly("大雁");
bird.birdFly("鸵鸟");
}
}
/**
** 输出大雁用翅膀飞和鸵鸟用翅膀飞,可是鸵鸟是不会飞的。
**/
//改进
public class Bird {
public void birdFly(String name){
if("鸵鸟".equals(name)){
System.out.println(name + "用脚走");
}else {
System.out.println(name + "用翅膀飞");
}
}
}
/**
** 可是如果再写入企鹅...的动物呢,那Bird类不就要加入更多的判断?
**/
//引入类的单一原则
public class FlyBird {
public void birdFly(String name){
System.out.println(name + "用翅膀飞");
}
}
public class WalkBird {
public void birdWalk(String name){
System.out.println(name + "用脚走");
}
}
public class Test {
public static void main(String[] args) {
/*Bird bird = new Bird();
bird.birdFly("大雁");
bird.birdFly("鸵鸟");*/
FlyBird flyBird = new FlyBird();
flyBird.birdFly("大雁");
WalkBird walkBird = new WalkBird();
walkBird.birdWalk("鸵鸟");
}
}
/**
** 将Bird类细分为会飞的和不会飞的,这样就能使每个类各自负责各自的职责。
**/
类的单一原则UML
1559731544380.png接口和方法的单一原则也是和类一样,尽量使其负责一个职责,这样修改其中一个代码的时候就不会涉及到其他代码了
4.接口隔离原则
定义:用多个专门的接口,而不使用单一的总接口
核心:细化接口,注意适度原则
//设计接口
public interface IAnimalsAction {
void eat();
void fly();
void walk();
}
//设计实现类
public class Dog implements IAnimalsAction {
@Override
public void eat() {
}
@Override
public void fly() {
}
@Override
public void walk() {
}
}
/**
** 会发现如果一旦实现了接口,就必须实现接口方法。可是Dog是不能飞的,所以就** 会存在着空实现。所以就需要用到接口隔离原则,细化接口
**/
//改进
public interface IEatAnimalsAction {
void eat();
}
public interface IFlyAnimalsAction {
void fly();
}
public interface ISwimAnimalsAction {
void swimming();
}
public class Dog implements IEatAnimalsAction, ISwimAnimalsAction {
@Override
public void eat() {
}
@Override
public void swimming() {
}
}
public class Bird implements IFlyAnimalsAction, IEatAnimalsAction {
@Override
public void eat() {
}
@Override
public void fly() {
}
}
/**
** 讲AnimalsAction接口细分话,将每一种职责细分,例如吃,吃有很多吃法,主** 要的吃法放在吃的接口上,同理,其他职责也一样。但是接口不能太细,这样就会** 有太多的接口,使程序复杂化。要做到用最少的接口方法,去完成最多的事情。
**/
接口隔离原则UML
1559728740561.png5.迪米特原则
定义:一个对象应该对其他对象保持最少的了解。
核心:尽量降低类与类之间的耦合。
业务描述:老板跟经理说想知道有多少职员。
//boss类
public class Boss {
public void commandCountNum(Manager manager){
List<Staff> staffList = new ArrayList<>();
for(int i = 1; i <= 100; i++){
staffList.add(new Staff());
}
manager.countStaffNum(staffList);
}
}
//经理类
public class Manager {
public void countStaffNum(List staffList){
System.out.println("一共有员工:" + staffList.size() + "名");
}
}
//员工类
public class Staff {
}
public class Test {
public static void main(String[] args) {
Boss boss = new Boss();
Manager manager = new Manager();
boss.commandCountNum(manager);
}
}
/**
** 根据业务描述,我们可以知道老板是不了解员工的,只有经理才了解员工,可以下述UML图反映老板和员工也有联** 系
**/
1559731213681.png
而迪米特原则就是让一个类尽量少了解其他类。
改进
public class Boss {
public void commandCountNum(Manager manager){
/*List<Staff> staffList = new ArrayList<>();
for(int i = 1; i <= 100; i++){
staffList.add(new Staff());
}*/
manager.countStaffNum();
}
}
public class Manager {
public void countStaffNum(){
List<Staff> staffList = new ArrayList<>();
for(int i = 1; i <= 100; i++){
staffList.add(new Staff());
}
System.out.println("一共有员工:" + staffList.size() + "名");
}
}
public class Staff {
}
public class Test {
public static void main(String[] args) {
Boss boss = new Boss();
Manager manager = new Manager();
boss.commandCountNum(manager);
}
}
/**
** 老板只需要告诉经理,而查询的任务交给经理来做。
**/
改进后的UML
1559731496709.png6.里氏替换原则
定义:一个软件实体如果适用一个父类的话,那一定适用于其子类,所有引用父类的地方必须能透明地使用其子类的对象,子类对象能够替换父类对象,而程序逻辑不变。
含义1:子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法
含义2:子类中可以增加自己特有的方法。
含义3:当子类的方法重载父类的方法时,方法的前置条件(即方法的输入/入参)要比父类方法的输入参数更宽松。
含义4:当子类的方法实现父类的方法时(重写/重载或实现抽象方法),方法的后置条件(即方法的输出/返回值)要比父类更严格或相等。
举出不符合里氏替换原则例子,假定正方形是一种特殊的长方形
public class Rectangle {
private int width;
private int height;
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
}
public class Square extends Rectangle {
private int sideLength;
@Override
public int getWidth() {
return getSideLength();
}
@Override
public void setWidth(int width) {
setSideLength(width);
}
@Override
public int getHeight() {
return getSideLength();
}
@Override
public void setHeight(int height) {
setSideLength(height);
}
public int getSideLength() {
return sideLength;
}
public void setSideLength(int sideLength) {
this.sideLength = sideLength;
}
}
public class Test {
public static void resize(Rectangle rectangle){
while(rectangle.getWidth() <= rectangle.getHeight()){
rectangle.setWidth(rectangle.getWidth() + 1);
System.out.println("width:" + rectangle.getWidth() + " length:" + rectangle.getHeight());
}
}
public static void main(String[] args) {
Rectangle rectangle = new Rectangle();
rectangle.setWidth(10);
rectangle.setHeight(20);
resize(rectangle);
Square square = new Square();
square.setSideLength(10);
resize(square);
}
}
/**
** 分别实例化了父类和子类对象,根据里氏替换原则定义,子类对象能够替换父类对** 象,而程序逻辑不变。可是程序中子类替换了父类后程序发生改变。所以这不符合** 里氏替换原则。
**/
7.组合/聚合复用原则
定义:尽量使用对象组合/聚合,而不是继承关系达到软件复用的目的。
核心:使用聚合has-A和组合contains-A
public class DBConnection {
public String getConnection(){
return "Mysql数据库连接";
}
}
public class ProductDao extends DBConnection {
public void addProduct(){
System.out.println("使用了" + super.getConnection() + "添加了产品");
}
}
public class Test {
public static void main(String[] args) {
ProductDao productDao = new ProductDao();
productDao.addProduct();
}
}
/**
** 如果使用其他数据库则需要改动DBConnection和ProductDao,违背了开闭原** 则。
**/
此时的UML
1559748273923.png使用组合/聚合复用原则改进
//改进为抽象类
public abstract class DBConnection {
public abstract String getConnection();
}
public class MysqlConnection extends DBConnection {
@Override
public String getConnection() {
return "Mysql数据库连接";
}
}
public class OracleConnection extends DBConnection {
@Override
public String getConnection() {
return "Oracle数据库连接";
}
}
public class ProductDao{
private DBConnection dbConnection;
public void setDbConnection(DBConnection dbConnection) {
this.dbConnection = dbConnection;
}
public void addProduct(){
System.out.println("使用了" + dbConnection.getConnection() + "添加了产品");
}
}
public class Test {
public static void main(String[] args) {
ProductDao productDao = new ProductDao();
productDao.setDbConnection(new OracleConnection());
productDao.addProduct();
}
}
/**
** 更改数据库则直接在productDao.setDbConnection的参数设成想要的数据库** 连接
**/
改进后的UML
1559748880573.png二、设计模式
认识简单工厂(不属于GOF23种设计模式)
定义:由一个工厂对象决定创建出哪一种产品类的实例
public abstract class Video {
public abstract void produce();
}
public class JavaVideo extends Video {
@Override
public void produce() {
System.out.println("生产Java视频");
}
}
public class PythonVideo extends Video {
@Override
public void produce() {
System.out.println("生产Python视频");
}
}
//定义一个工厂类
public class VideoFactory {
public Video getVideo(String str){
if(str.equalsIgnoreCase("java")){
return new JavaVideo();
}else if(str.equalsIgnoreCase("python")){
return new PythonVideo();
}
return null;
}
}
public class Test {
public static void main(String[] args) {
VideoFactory videoFactory = new VideoFactory();
Video video = videoFactory.getVideo(PythonVideo.class);
video.produce();
}
}
/**
** 通过工厂类,返回外界所需要的具体对象。不需要管这个对象是如何创建以及如何组织的。但不满** 足开闭原则,因为这个工厂所能实例化的类是事先考虑到的,如果需求改变,需要修改工厂类或添加一个新的工厂** 类
**/
UML
1559821941660.png1.工厂方法
定义:定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类工厂方法让类的实例化推迟到子类中进行。
使用范围:1)创建对象需要大量重复的代码。2)客户端(应用层)不依赖于产品类实例如何被创建、实现等细节。3)一个类通过其子类来指定创建哪个对象。
优点:1)用户只需要关心所需产品对应的工厂,无须关系创建细节。2)加入新产品符合开闭原则,提高可扩展性。
缺点:1)类的个数容易过多,增加复杂度。2)增加了系统的抽象性和理解难度。
类型:创建型
public abstract class Video {
public abstract void produce();
}
public class JavaVideo extends Video {
@Override
public void produce() {
System.out.println("生产Java视频");
}
}
public class PythonVideo extends Video {
@Override
public void produce() {
System.out.println("生产Python视频");
}
}
public abstract class VideoFactory {
public abstract Video getVideo();
}
public class JavaVideoFactory extends VideoFactory {
@Override
public Video getVideo() {
return new JavaVideo();
}
}
public class PythonVideoFactory extends VideoFactory {
@Override
public Video getVideo() {
return new PythonVideo();
}
}
public class Test {
public static void main(String[] args) {
VideoFactory pythonVideoFactory = new PythonVideoFactory();
Video video = pythonVideoFactory.getVideo();
video.produce();
VideoFactory javaVideoFactory = new JavaVideoFactory();
Video video1 = javaVideoFactory.getVideo();
video1.produce();
}
}
/**
** 解决了简单工厂不能满足开闭原则的问题。如果添加一个视频只需新建一个类继承Video抽象类即可。工厂方法** 关注产品等级结构
** /
UML
1559814329429.png2.抽象工厂
定义:抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口
适用场景:1)强调一系列相关的产品对象(属于同一产品族)一起使用创建对象需要大量重复的代码。这时可以将同一个产品族放在一个类中管理。2)客户端(应用层)不依赖产品类实例如何被创建、实现等细节。3)提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现。
优点:1)具体产品在应用层代码隔离,无须关心创建细节。2)将一系列的产品族统一到一起创建。
缺点:1)规定了所有可能被创建的产品集合,产品族中扩展新的产品困难,需要修改抽象工厂的接口。2)增加了系统的抽象性和理解难度。
名词解释:产品族:例如美的有空调、洗衣机等,而美的就是一个产品族。产品等级结构:空调有美的牌的、海尔牌的等,而空调的不同品牌就是一个产品等级结构。
类型:创建型
public abstract class Article {
public abstract void produce();
}
public abstract class Video {
public abstract void produce();
}
public class JavaArticle extends Article {
@Override
public void produce() {
System.out.println("写Java手记");
}
}
public class JavaVideo extends Video {
@Override
public void produce() {
System.out.println("录Java视频");
}
}
public class PythonArticle extends Article {
@Override
public void produce() {
System.out.println("写Python手记");
}
}
public class PythonVideo extends Video {
@Override
public void produce() {
System.out.println("录Python视频");
}
}
public interface CourseFactory {
Video getVideo();
Article getArticle();
}
public class JavaCourseFactory implements CourseFactory {
@Override
public Video getVideo() {
return new JavaVideo();
}
@Override
public Article getArticle() {
return new JavaArticle();
}
}
public class PythonCourseFactory implements CourseFactory {
@Override
public Video getVideo() {
return new PythonVideo();
}
@Override
public Article getArticle() {
return new PythonArticle();
}
}
public class Test {
public static void main(String[] args) {
CourseFactory javaCourseFactory = new JavaCourseFactory();
Video javaVideo = javaCourseFactory.getVideo();
javaVideo.produce();
Article javaArticle = javaCourseFactory.getArticle();
javaArticle.produce();
CourseFactory pythonCourseFactory = new PythonCourseFactory();
Article pythonArticle = pythonCourseFactory.getArticle();
pythonArticle.produce();
Video pythonVideo = pythonCourseFactory.getVideo();
pythonVideo.produce();
}
}
/**
** 减少工厂方法的类数量。更容易管理。利于扩展新的产品族。但如果产品族里需要添加产品,则需要添加很多代** 码。抽象工厂关注产品族。
**/
UML
1559822988412.png3.建造者模式
定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
使用场景:1)如果一个对象有非常复杂的内部结构(很多属性)2)想把复杂对象的创建和使用分离
优点:1)封装性好,创建和使用分离。2)扩展性好、建造者之间独立、一定程度上解耦。
缺点:1)产生多余的Builder对象。2)产品内部发生变化,建造者都要修改,成本较大。
类型:创建型
//V1版本
public class Course {
private String courseName;
private String courseVideo;
private String courseArticle;
private String coursePPT;
//...省略get set方法和toString方法
}
//抽象类
public abstract class CourseBuilder {
public abstract void buildCourseName(String courseName);
public abstract void buildCourseVideo(String courseVideo);
public abstract void buildCourseArticle(String courseArticle);
public abstract void buildCoursePPT(String coursePPT);
public abstract Course makeCourse();
}
public class CourseActualBuilder extends CourseBuilder {
private Course course = new Course();
@Override
public void buildCourseName(String courseName) {
this.course.setCourseName(courseName);
}
@Override
public void buildCourseVideo(String courseVideo) {
this.course.setCourseVideo(courseVideo);
}
@Override
public void buildCourseArticle(String courseArticle) {
this.course.setCourseArticle(courseArticle);
}
@Override
public void buildCoursePPT(String coursePPT) {
this.course.setCoursePPT(coursePPT);
}
@Override
public Course makeCourse(){
return this.course;
}
}
//负责建造的类
public class ResponsiblePerson {
private CourseBuilder courseBuilder;
public void setCourseBuilder(CourseBuilder courseBuilder) {
this.courseBuilder = courseBuilder;
}
public Course makeCourse(String courseName, String courseVideo, String courseArticle, String coursePPT){
this.courseBuilder.buildCourseName(courseName);
this.courseBuilder.buildCourseVideo(courseVideo);
this.courseBuilder.buildCourseArticle(courseArticle);
this.courseBuilder.buildCoursePPT(coursePPT);
return courseBuilder.makeCourse();
}
}
public class Test {
public static void main(String[] args) {
ResponsiblePerson responsiblePerson = new ResponsiblePerson();
CourseBuilder courseActualBuilder = new CourseActualBuilder();
responsiblePerson.setCourseBuilder(courseActualBuilder);
Course course = responsiblePerson.makeCourse("Java基础", "Java基础视频", "Java基础手记", "Java基础PPT");
System.out.println(course);
}
}
UML
1559886739032.png//V2版本(内部类)
public class Course {
private String courseName;
private String courseVideo;
private String courseArticle;
private String coursePPT;
public Course(CourseBuilder courseBuilder){
this.courseName = courseBuilder.courseName;
this.courseVideo = courseBuilder.courseVideo;
this.courseArticle = courseBuilder.courseArticle;
this.coursePPT = courseBuilder.coursePPT;
}
@Override
public String toString() {
return "Course{" +
"courseName='" + courseName + '\'' +
", courseVideo='" + courseVideo + '\'' +
", courseArticle='" + courseArticle + '\'' +
", coursePPT='" + coursePPT + '\'' +
'}';
}
public static class CourseBuilder{
private String courseName;
private String courseVideo;
private String courseArticle;
private String coursePPT;
//方法返回CourseBuilder的作用是链式调用。
public CourseBuilder buildCourseName(String courseName){
this.courseName = courseName;
return this;
}
public CourseBuilder buildcourseVideo(String courseVideo){
this.courseVideo = courseVideo;
return this;
}
public CourseBuilder buildCourseArticle(String courseArticle){
this.courseArticle = courseArticle;
return this;
}
public CourseBuilder buildCoursePPT(String coursePPT){
this.coursePPT = coursePPT;
return this;
}
public Course build(){
return new Course(this);
}
}
}
public class Test {
public static void main(String[] args) {
Course course = new Course.CourseBuilder().buildCourseName("Java基础").buildCourseArticle("Java基础手记").buildcourseVideo("Java基础视频").build();
System.out.println(course);
}
}
改进后的UML
1559887479735.png4.单例模式
定义:保证一个类仅有一个实例,并提供一个全局访问点。
适用场景:1)想确保任何情况下都绝对只有一个实例
优点:1)在内存里只有一个实例,减少了内存的开销。2)可以避免对资源的多重占用。3)设置全局访问点,严格控制访问。
缺点:1)没有接口,扩展困难
单例重点:1)私有构造器。2)线程安全。3)延迟加载。4)序列化和反序列化安全。5)反射。
类型:创建型
①懒汉式(类加载的时候不创建对象)
public class LazySingleton {
private static LazySingleton lazySingleton = null;
private LazySingleton(){}
//同步,防止出现类的多实例情况
public synchronized static LazySingleton getInstance(){
if(lazySingleton == null){
lazySingleton = new LazySingleton();
}
return lazySingleton;
}
}
//加入多线程
public class T implements Runnable {
@Override
public void run() {
LazySingleton lazySingleton = LazySingleton.getInstance();
System.out.println(lazySingleton);
}
}
public class Test{
public static void main(String[] args) {
Thread t1 = new Thread(new T());
Thread t2 = new Thread(new T());
t1.start();
t2.start();
System.out.println("结束");
}
}
-->加入DoubleCheck双重检查(存在类实例化时的重排序问题)
//对单例类进行改造
public class LazyDoubleCheckSingleton {
//加入volatile关键字,即可实现线程安全的延迟初始化,禁止了重排序,所有线程都能看到共享内存的最新状态。
private volatile static LazyDoubleCheckSingleton lazyDoubleCheckSingleton = null;
private LazyDoubleCheckSingleton(){}
public static LazyDoubleCheckSingleton getInstance(){
if(lazyDoubleCheckSingleton == null){
synchronized (LazyDoubleCheckSingleton.class){
if(lazyDoubleCheckSingleton == null){
//类实例步骤:
//1.分配内存给这个对象
//2.初始化对象
//3.设置lazyDoubleCheckSingleton指向刚分配的内存空间地址
//其中2和3有可能会顺序颠倒,所以在多线程中会出现问题,一个线程可能拿到的是一个没有初始化完整的对象。
lazyDoubleCheckSingleton = new LazyDoubleCheckSingleton();
}
}
}
return lazyDoubleCheckSingleton;
}
}
②使用私有静态内部类
public class LazyInnerClassSingleton {
private LazyInnerClassSingleton(){}
//内部类
private static class InnerClass{
private static LazyInnerClassSingleton lazyInnerClassSingleton = new LazyInnerClassSingleton();
}
public static LazyInnerClassSingleton getInstance(){
return InnerClass.lazyInnerClassSingleton;
}
}
③饿汉式(类加载的时候直接创建对象)
public class HungrySingleton {
private final static HungrySingleton hungrySingleton;
static{
hungrySingleton = new HungrySingleton();
}
public static HungrySingleton getInstance(){
return hungrySingleton;
}
}
④序列化与反序列化对单例的破坏
//单例类需实现serializable接口,不然会报错。
public static void main(String[] args) throws Exception {
HungrySingleton hungrySingleton = HungrySingleton.getInstance();
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("hungry_singletion"));
oos.writeObject(hungrySingleton);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("hungry_singletion"));
HungrySingleton newHungrySingleton = (HungrySingleton) ois.readObject();
System.out.println(hungrySingleton);
System.out.println(newHungrySingleton);
System.out.println(hungrySingleton == newHungrySingleton);
}
//控制台会输出false。结果就是序列化和反序列化会破坏单例。
//解决办法:
public class HungrySingleton implements Serializable {
private final static HungrySingleton hungrySingleton;
static{
hungrySingleton = new HungrySingleton();
}
public static HungrySingleton getInstance(){
return hungrySingleton;
}
//关键
public Object readResolve(){
return hungrySingleton;
}
}
⑤反射攻击解决方案
饿汉式解决方案
//未采取解决方案可通过反射创建对象
public static void main(String[] args) throws Exception {
HungrySingleton instance = HungrySingleton.getInstance();
Class clazz = Class.forName(HungrySingleton.class.getName());
Constructor declaredConstructor = clazz.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
HungrySingleton newInstance = (HungrySingleton)declaredConstructor.newInstance();
System.out.println(instance);
System.out.println(newInstance);
}
//输出的地址不相同,即攻击成功
//解决方案
public class HungrySingleton /*implements Serializable*/{
private final static HungrySingleton hungrySingleton;
static{
hungrySingleton = new HungrySingleton();
}
private HungrySingleton(){
//解决方案
if(hungrySingleton != null){
throw new RuntimeException("不允许通过反射创建对象!");
}
}
public static HungrySingleton getInstance(){
return hungrySingleton;
}
/*public Object readResolve(){
return hungrySingleton;
}*/
}
内部类解决方案(类加载的时候直接实例化对象)
//未采取解决方案可通过反射创建对象
public static void main(String[] args) throws Exception {
LazyInnerClassSingleton instance = LazyInnerClassSingleton.getInstance();
Class clazz = LazyInnerClassSingleton.class;
Constructor declaredConstructor = clazz.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
LazyInnerClassSingleton newInstance = (LazyInnerClassSingleton) declaredConstructor.newInstance();
System.out.println(instance);
System.out.println(newInstance);
}
//输出的地址不相同,即攻击成功
public class LazyInnerClassSingleton {
private LazyInnerClassSingleton(){
//解决方案
if(InnerClass.lazyInnerClassSingleton != null){
throw new RuntimeException("不允许通过反射创建对象!");
}
}
private static class InnerClass{
private static LazyInnerClassSingleton lazyInnerClassSingleton = new LazyInnerClassSingleton();
}
public static LazyInnerClassSingleton getInstance(){
return InnerClass.lazyInnerClassSingleton;
}
}
//解决方案和饿汉式一样
对于懒汉式方式的话,是无法解决反射攻击的。
⑥枚举单例(可以解决反射攻击以及序列化反序列化问题)
public enum EnumSingleton {
INSTANCE;
private Object data;
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public static EnumSingleton getInstance(){
return INSTANCE;
}
}
public static void main(String[] args) throws Exception {
//测试序列化与反序列化攻击
EnumSingleton instance = EnumSingleton.getInstance();
instance.setData(new Object());
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("enum_singletion"));
oos.writeObject(instance);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("enum_singletion"));
EnumSingleton newInstance = (EnumSingleton) ois.readObject();
System.out.println(instance.getData());
System.out.println(newInstance.getData());
System.out.println(instance.getData() == newInstance.getData());
//输出结果为true,即序列化与反序列化对枚举单例没有作用。
//测试反射攻击
Class clazz = EnumSingleton.class;
Constructor constructor = clazz.getDeclaredConstructor(String.class, int.class);//Enum枚举底层只有1个带2个参数的构造器
/*
protected Enum(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
}
*/
constructor.setAccessible(true);
EnumSingleton enumSingleton = (EnumSingleton) constructor.newInstance();
//异常:Cannot reflectively create enum objects
//底层源码:
/*if ((clazz.getModifiers() & Modifier.ENUM) != 0)
throw new IllegalArgumentException("Cannot reflectively create enum objects");*/
System.out.println(enumSingleton);
}
⑦容器单例(线程问题可能会导致对象实例话多次)
public class ContainerSingleton {
private static Map<String, Object> singletonMap = new HashMap<>();
private ContainerSingleton(){}
public static void putInstance(String key, Object obj){
if(StringUtils.isNotBlank(key) && obj != null){
if(!singletonMap.containsKey(key)){
singletonMap.put(key, obj);
}
}
}
public static Object getInstance(String key){
return singletonMap.get(key);
}
}
⑧ThreadLocal单例(保证每个线程的对象是唯一的)
public class ThreadLocalSingleton {
private static final ThreadLocal<ThreadLocalSingleton> THREAD_LOCAL = new ThreadLocal<ThreadLocalSingleton>(){
@Override
protected ThreadLocalSingleton initialValue() {
return new ThreadLocalSingleton();
}
};
private ThreadLocalSingleton(){};
public static ThreadLocalSingleton getInstance(){
return THREAD_LOCAL.get();
}
}
5.原型模式
定义:指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
核心:不需要知道任何创建的细节,不调用构造函数。
适用场景:1)类初始化消耗较多资源。2)new产生的一个对象需要非常繁琐的过程(数据准备、访问权限等)。3)构造函数比较复杂。4)循环体中生产大量对象。
优点:1)原型模式性能比直接new一个对象性能高。2)简化创建过程
缺点:1)必须配备克隆方法。2)对克隆复杂对象或对克隆出的对象进行复杂改造时,容易引入风险。3)深拷贝、浅拷贝要运用得当。
类型:创建型
业务描述:需要向10名同学发送中奖的邮箱信息。邮箱信息有个初始化模板,给10名同学发送信息后,初始模板不会被修改。
//未引入原型模式
public class Mail{
private String name;
private String mailAddress;
private String context;
//get、set方法省略
}
public class MailUtil {
public static void sendMail(Mail mail){
String sendMsg = "向{0}同学,邮件地址:{1},邮件内容:{2}发送邮件";
System.out.println(MessageFormat.format(sendMsg, mail.getName(), mail.getMailAddress(), mail.getContext()));
}
public static void saveOriginMailRecord(Mail mail){
System.out.println("存储原始邮件的记录:" + mail.getContext());
}
}
public static void main(String[] args) throws CloneNotSupportedException {
Mail mail = new Mail();
mail.setContext("初始化模板");
for(int i = 1; i <= 10; i++){
//需要引入大量的对象
Mail newMail = new Mail();
newMail.setName("姓名" + i);
newMail.setMailAddress("姓名" + i + "@qq.com");
newMail.setContext("恭喜您 中奖了!");
MailUtil.sendMail(newMail);
}
MailUtil.saveOriginMailRecord(mail);
}
//引入原型模式
public class Mail implements Cloneable{
private String name;
private String mailAddress;
private String context;
//省略get、set方法
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public static void main(String[] args) throws CloneNotSupportedException {
Mail mail = new Mail();
mail.setContext("初始化模板");
for(int i = 1; i <= 10; i++){
Mail cloneMail = (Mail) mail.clone();//在内存中进行二进制流的拷贝,比new高效。
cloneMail.setName("姓名" + i);
cloneMail.setMailAddress("姓名" + i + "@qq.com");
cloneMail.setContext("恭喜您 中奖了!");
MailUtil.sendMail(cloneMail);
}
MailUtil.saveOriginMailRecord(mail);
}
①浅克隆(不是引用类型)
public class Person implements Cloneable{
private String name;
private int age;
private Date birthday;
//省略get、set方法
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", birthday=" + birthday +
'}' + super.toString();
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public static void main(String[] args) throws CloneNotSupportedException {
Person person = new Person();
person.setName("cjw");
person.setAge(21);
person.setBirthday(new Date());
Person clone = (Person) person.clone();
System.out.println(person);
System.out.println(clone);
System.out.println("修改clone对象的属性-----");
clone.setName("kk");
clone.setAge(22);
System.out.println(person);
System.out.println(clone);
}
//控制台输出如下,可以发现修改第二个的对象属性值不会影响第一个对象。
1559981744255.png
②深克隆(存在引用类型)
//上述代码存在引用当修改引用类型的会出现什么呢?
public static void main(String[] args) throws CloneNotSupportedException {
Person person = new Person();
person.setName("cjw");
person.setAge(21);
person.setBirthday(new Date(1L));
Person clone = (Person) person.clone();
System.out.println(person);
System.out.println(clone);
System.out.println("修改clone对象的属性-----");
clone.setName("kk");
clone.setAge(22);
clone.getBirthday().setTime(66666L);//修改引用类型
System.out.println(person);
System.out.println(clone);
}
//下图可以发现两个的引用类型都同时修改了
1559982408008.png
我们可以debug看看
1559982525332.png我们发现两个对象的引用类型的地址是一致的,这就是为什么修改一个对象的引用类型的值,两个对象都会一起修改。
//深拷贝
public class Person implements Cloneable{
private String name;
private int age;
private Date birthday;
//get、set方法省略
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", birthday=" + birthday +
'}' + super.toString();
}
@Override
protected Object clone() throws CloneNotSupportedException {
Person clone = (Person) super.clone();
//深克隆
clone.birthday = new Date();
return clone;
}
}
补:序列化与反序列化也可以实现深拷贝。原型模式可以破坏单例模式,例如:饿汉模式的类实现了Cloneable接口后,通过反射获取clone方法是可以破坏单例的。
6.外观模式
定义:又叫门面模式,提供了一个统一的接口,用来访问子系统中的一群接口。
核心:外观模式定义了一个高层接口,让子系统更容易使用。
适用场景:1)子系统越来越复杂,增加外观模式提供简单调用接口。2)构建多层系统结构,利用外观对象作为每层的入口,简化层间调用。
优点:1)简化了调用过程,无需了解深入子系统,防止带来风险。2)减少系统依赖、松散耦合。3)更好的划分访问层次。4)符合迪米特法则,即最少知道原则。
缺点:1)增加子系统、扩展子系统行为容易引入风险。2)不符合开闭原则。
类型:结构型
业务描述:有一个积分兑换业务,其中包括了校验子系统、兑换子系统、物流子系统
//未引入外观模型(不考虑具体实现)
//积分礼物
public class PointGift {
private String name;//礼物名称
public PointGift(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
//校验子系统
public class QualifyService {
public boolean isAvailable(PointGift pointGift){
System.out.println("校验" + pointGift.getName() + ",积分验证通过");
return true;
}
}
//兑换子系统
public class PointsPaymentService {
public boolean pay(PointGift pointGift){
System.out.println("支付" + pointGift.getName() + "礼物所需积分成功");
return true;
}
}
//物流子系统
public class ShippingService {
public String shipGift(PointGift pointGift){
System.out.println(pointGift.getName() + "进入物流系统");
String shippingOrderNo = "666";
return shippingOrderNo;
}
}
//测试
public static void main(String[] args) {
PointGift pointGift = new PointGift("鼠标");
QualifyService qualifyService = new QualifyService();
if(qualifyService.isAvailable(pointGift)){
PointsPaymentService pointsPaymentService = new PointsPaymentService();
if(pointsPaymentService.pay(pointGift)){
ShippingService shippingService = new ShippingService();
String s = shippingService.shipGift(pointGift);
System.out.println("订单号:" + s);
}
}
}
//从下图可以知道应用层(Test)是依赖每一个子系统的。而往往高层是不需要关心礼物兑换的具体实现。
1559988097313.png
//引入外观模式
//将涉及到的子系统封装到一个类中,应用层只需调用exchangeGift即可,不需要关心兑换的细节
public class PointGiftExchangeService {
private QualifyService qualifyService = new QualifyService();
private PointsPaymentService pointsPaymentService = new PointsPaymentService();
private ShippingService shippingService = new ShippingService();
public void exchangeGift(PointGift pointGift){
if(qualifyService.isAvailable(pointGift)){
if(pointsPaymentService.pay(pointGift)){
String orderNo = shippingService.shipGift(pointGift);
System.out.println("订单号:" + orderNo);
}
}
}
}
public static void main(String[] args) {
PointGift pointGift = new PointGift("鼠标");
PointGiftExchangeService pointGiftExchangeService = new PointGiftExchangeService();
pointGiftExchangeService.exchangeGift(pointGift);
}
引入外观模式的UML
1559988739191.png可以看到应用层(Test)是不需要关心子系统是如何实现功能的。而具体的实现交由外观类来实现。
7.装饰者模式(外观模式)
定义:在不改变原有对象的基础之上,将功能附加到对象上。
核心:提供了比继承更有弹性的替代方案(扩展原有对象功能)。
类型:结构型
适用场景:1)扩展一个类的功能或给一个类添加附加职责。2)动态的给一个对象添加功能,这些功能可以再动态的撤销。
优点:1)继承的有力补充,比继承灵活,不改变原有对象的情况下给一个对象扩展功能。2)通过使用不同装饰类以及这些装饰类的排列组合,可以实现不同效果。3)符合开闭原则。
缺点:1)会出现更多的代码,更多的类,增加程序复杂性。2)动态装饰时,多层装饰时会更复杂。
业务描述:几个人去吃煎饼,每个人的要求不一样,有人只要饼,有的人要加蛋有的人要加肠等。
//未引入装配者模式
public class BatterCake {
protected String getDesc(){
return "煎饼";
}
protected int getPrice(){
return 8;
}
}
public class BatterCakeWithEgg extends BatterCake {
@Override
protected String getDesc() {
return super.getDesc() + " 加一个蛋";
}
@Override
protected int getPrice() {
return super.getPrice() + 1;
}
}
public class BatterCakeWithEggSausage extends BatterCakeWithEgg{
@Override
protected String getDesc() {
return super.getDesc() + " 加一根肠";
}
@Override
protected int getPrice() {
return super.getPrice() + 2;
}
}
public class Test {
public static void main(String[] args) {
//只要煎饼
BatterCake batterCake = new BatterCake();
System.out.println("搭配:" + batterCake.getDesc() + " 价格:" + batterCake.getPrice());
//加个鸡蛋
BatterCakeWithEgg batterCakeWithEgg = new BatterCakeWithEgg();
System.out.println("搭配:" + batterCakeWithEgg.getDesc() + " 价格:" + batterCakeWithEgg.getPrice());
//加个鸡蛋加个肠
BatterCakeWithEggSausage batterCakeWithEggSausage = new BatterCakeWithEggSausage();
System.out.println("搭配:" + batterCakeWithEggSausage.getDesc() + " 价格:" + batterCakeWithEggSausage.getPrice());
}
}
//思考:万一需要加10个鸡蛋呢?如果按照上面的实现就要新建很多很多的加鸡蛋类,导致程序复杂。
此时的UML
1559992661037.png//引入装饰者模式
public abstract class ABatterCake {
protected abstract String getDesc();
protected abstract int getPrice();
}
public class BatterCake extends ABatterCake {
@Override
protected String getDesc() {
return "煎饼";
}
@Override
protected int getPrice() {
return 8;
}
}
public class AbstractDecorator extends ABatterCake {
private ABatterCake aBatterCake;
public AbstractDecorator(ABatterCake aBatterCake) {
this.aBatterCake = aBatterCake;
}
@Override
protected String getDesc() {
return aBatterCake.getDesc();
}
@Override
protected int getPrice() {
return aBatterCake.getPrice();
}
}
public class EggDecorator extends AbstractDecorator{
public EggDecorator(ABatterCake aBatterCake) {
super(aBatterCake);
}
@Override
protected String getDesc() {
return super.getDesc() + " 加一个蛋";
}
@Override
protected int getPrice() {
return super.getPrice() + 1;
}
}
public class SausageDecorator extends AbstractDecorator {
public SausageDecorator(ABatterCake aBatterCake) {
super(aBatterCake);
}
@Override
protected String getDesc() {
return super.getDesc() + " 加一根香肠";
}
@Override
protected int getPrice() {
return super.getPrice() + 2;
}
}
public class Test {
public static void main(String[] args) {
ABatterCake aBatterCake = new BatterCake();
//加2个蛋一根肠
aBatterCake = new EggDecorator(aBatterCake);
aBatterCake = new EggDecorator(aBatterCake);
aBatterCake = new SausageDecorator(aBatterCake);
System.out.println("搭配:" + aBatterCake.getDesc() + " 价格:" + aBatterCake.getPrice());
}
}
//这样就能实现加多个鸡蛋或香肠,或者加其他东西的功能。
引入装饰模式的UML
1559993427781.png8.适配器模式
定义:将一个类的接口转换成客户期望的另一个接口。
核心:使原本接口不兼容的类可以一起工作。
类型:结构型
适用场景:1)已经存在的类,它的方法和需求不匹配时(方法结果相同或相似)。2)不是软件涉及阶段考虑的涉及模式,是随着软件维护,由于不同产品、不同厂家造成功能类似而接口不相同情况下的解决方案。
优点:1)能提高类的透明性和复用,现有的类复用但不需要改变。2)目标类和适配器类解耦,提高程序扩展性。3)符合开闭原则。
缺点:1)适配器编写过程需要全面考虑,可能会增加系统的复杂性。2)增加系统代码可读的难度。
①类适配器模式
//目标方法接口
public interface Target {
void request();
}
//目标方法的具体实现
public class ConcreteTarget implements Target {
@Override
public void request() {
System.out.println("ConcreteTarget的request方法");
}
}
//被适配者方法
public class Adaptee {
public void adapteeRequest(){
System.out.println("被适配者的方法");
}
}
//适配者
public class Adapter extends Adaptee implements Target {
@Override
public void request() {
super.adapteeRequest();
}
}
public class Test {
public static void main(String[] args) {
//Target具体的目标方法
Target target = new ConcreteTarget();
target.request();
//Target的具体实现通过Adapter适配到Adaptee
Adapter adapter = new Adapter();
adapter.request();
}
}
UML
1559995622782.png②对象适配者模式
//修改Adapter类,不使用继承关系
public class Adapter implements Target {
private Adaptee adaptee = new Adaptee();
@Override
public void request() {
adaptee.adapteeRequest();
}
}
UML
1559996051516.png③电源适配器例子
//交流电220V
public class AC220 {
public int outputAC220V(){
int output = 220;
System.out.println("输出交流电" + output + "V");
return output;
}
}
//直流电5V(转换目标)
public interface DC5 {
int outputDC5V();
}
//电源适配器
public class PowerAdapter implements DC5 {
private AC220 ac220 = new AC220();
@Override
public int outputDC5V() {
int adapterInput = ac220.outputAC220V();
int adapterOutput = adapterInput / 44;
System.out.println("使用PowerAdapter,输入" + adapterInput + "V, 输出" + adapterOutput + "V");
return adapterOutput;
}
}
public class Test {
public static void main(String[] args) {
DC5 dc5 = new PowerAdapter();
dc5.outputDC5V();
}
}
9.享元模式
定义:提供了减少对象数量从而改善应用所需的对象结构的方式。
核心:运用共享技术有效地支持大量细粒度的对象。
类型:结构型
适用场景:1)常常应用于系统底层的开发,以便解决系统的性能问题。2)系统有大量相似对象、需要缓冲池的场景。
优点:1)减少对象的创建,降低内存中对象的数量,降低系统的内存,提高效率。2)减少内存之外的其他资源占用。
缺点:1)关注内/外状态、关注线程安全问题。2)使系统、程序的逻辑复杂化。
业务描述:一个公司有很多部门,每个部门都有一个经理,经理需要做报告,可是每次做报告的部门不确定。
//员工接口类(经理也是员工)
public interface Employee {
void report();
}
//经理类
public class Manager implements Employee {
private String reportContent;
private String department;
public Manager(String department) {
this.department = department;
}
public void setReportContent(String reportContent) {
this.reportContent = reportContent;
}
@Override
public void report() {
System.out.println(reportContent);
}
}
//结合工厂模式单例模式
public class EmployeeFactory {
private final static Map<String, Employee> EMPLOYEE_MAP = new HashMap<>();
public static Employee getManager(String department){
Manager manager = (Manager) EMPLOYEE_MAP.get(department);
if(manager == null){
manager = new Manager(department);
System.out.print("创建部门经理:" + department);
String reportContent = department + "部门汇报:此次报告的主要内容....";
manager.setReportContent(reportContent);
System.out.println(" 创建报告:" + reportContent);
EMPLOYEE_MAP.put(department, manager);
}
return manager;
}
}
//测试
public class Test {
private static final String departments[] = {"RD", "QA", "PM", "BD"};
public static void main(String[] args) {
for(int i = 0; i < 10; i++){
String department = departments[(int)(Math.random() * departments.length)];
Manager manager = (Manager) EmployeeFactory.getManager(department);
manager.report();
}
}
}
//这样就能做到不同部门经理第一次写报告new实例,下次就直接取就好了。
UML
1560052045167.png10.组合模式
定义:将对象组合成树形结构以表示“部分-整体”的层次结构。
核心:组合模式事客户端对单个对象和组合对象保持一致的方式处理。
类型:结构型
适用场景:1)希望客户端可以忽略组合对象与单个对象的差异时。2)处理一个树形结构时。
优点:1)清楚地定义分层次的复杂对象,表示对象的全部或部分层次。2)让客户端忽略了层次的差异,方便对整个层次结构进行控制。3)简化客户端代码。4)符合开闭原则。
缺点:1)限制类型时会较为复杂。2)使设计变得更加抽象。
功能描述:制作一个课程目录,目录下有子课程目录或是课程
//目录组件(允许目录或文件能做什么不能做什么)
public abstract class CatalogComponent {
public void add(CatalogComponent catalogComponent){
throw new UnsupportedOperationException("不支持添加操作");
}
public void remove(CatalogComponent catalogComponent){
throw new UnsupportedOperationException("不支持删除操作");
}
public String getName(CatalogComponent catalogComponent){
throw new UnsupportedOperationException("不支持获取名称操作");
}
public double getPrice(CatalogComponent catalogComponent){
throw new UnsupportedOperationException("不支持获取价格操作");
}
public void print(){
throw new UnsupportedOperationException("不支持打印操作");
}
}
//课程(文件)
public class Course extends CatalogComponent {
private String name;
private double price;
public Course(String name, double price) {
this.name = name;
this.price = price;
}
@Override
public String getName(CatalogComponent catalogComponent) {
return this.name;
}
@Override
public double getPrice(CatalogComponent catalogComponent) {
return this.price;
}
@Override
public void print() {
System.out.println("Course Name:" + this.name + " Price:" + this.price);
}
}
//课程目录(目录)
public class CourseCatalog extends CatalogComponent {
private List<CatalogComponent> catalogComponentList = new ArrayList<>();
private String name;
private int level;
public CourseCatalog(String name, int level) {
this.name = name;
this.level = level;
}
@Override
public void add(CatalogComponent catalogComponent) {
catalogComponentList.add(catalogComponent);
}
@Override
public void remove(CatalogComponent catalogComponent) {
catalogComponentList.remove(catalogComponent);
}
@Override
public String getName(CatalogComponent catalogComponent) {
return this.name;
}
@Override
public void print() {
System.out.println(this.name);
for (CatalogComponent component : catalogComponentList) {
for (int i = 0; i < level; i++) System.out.print("\t");
component.print();
}
}
}
//测试
public class Test {
public static void main(String[] args) {
CatalogComponent linuxCourse = new Course("Linux课程", 11);
CatalogComponent windowsCourse = new Course("Windows课程", 22);
CatalogComponent javaCatalog = new CourseCatalog("Java课程目录", 2);
CatalogComponent fundamentalCourse = new Course("Java基础课", 33);
CatalogComponent designPatternCourse = new Course("Java设计模式", 44);
CatalogComponent projectPracticeCourse = new Course("Java项目实战", 55);
javaCatalog.add(fundamentalCourse);
javaCatalog.add(designPatternCourse);
javaCatalog.add(projectPracticeCourse);
CatalogComponent mainCatalog = new CourseCatalog("课程主目录", 1);
mainCatalog.add(linuxCourse);
mainCatalog.add(windowsCourse);
mainCatalog.add(javaCatalog);
mainCatalog.print();
}
}
/*控制台输出,以树状形式输出
课程主目录
Course Name:Linux课程 Price:11.0
Course Name:Windows课程 Price:22.0
Java课程目录
Course Name:Java基础课 Price:33.0
Course Name:Java设计模式 Price:44.0
Course Name:Java项目实战 Price:55.0
*/
1560057223430.png
11.桥接模式
定义:将抽象部分与它的具体实现部分分离,使他们都可以独立地变化。
核心:通过组合的方式建立两个类之间联系,而不是继承。
类型:结构型。
适用场景:1)抽象和具体实现之间增加更多的灵活性。2)一个类存在两个(或多个)独立变化的维度,且这两个(或多个)维度都需要独立进行扩展。3)不希望使用继承,或因为多层继承导致系统类的个数剧增。
优点:1)分离抽象部分以及具体实现部分。2)提高了系统的可扩展性。3)符合开闭原则。4)符合合成复用原则。
缺点:1)增加了系统的理解与设计难度。2)需要正确地识别出系统中两个独立变化的维度。
描述:中国有很多银行,例如:ICBC、ABC,每个银行也有很多账号,例如:定期账号、活期账号。
//账号接口类
public interface Account {
Account openAccount();
void showAccountType();
}
//定期账号
public class DepositAccount implements Account{
@Override
public Account openAccount() {
System.out.println("打开定期账号");
return new DepositAccount();
}
@Override
public void showAccountType() {
System.out.println("这个是定期账号");
}
}
//活期账号
public class SavingAccount implements Account {
@Override
public Account openAccount() {
System.out.println("打开活期账号");
return new SavingAccount();
}
@Override
public void showAccountType() {
System.out.println("这是一个活期账号");
}
}
//银行抽象类
public abstract class Bank {
protected Account account;
public Bank(Account account) {
this.account = account;
}
public abstract Account openAccount();
}
//工商银行类
public class ICBCBank extends Bank {
public ICBCBank(Account account) {
super(account);
}
@Override
public Account openAccount() {
System.out.println("打开中国工商银行账号");
account.openAccount();//桥接到指定账号的方法
return account;
}
}
//农业银行
public class ABCBank extends Bank {
public ABCBank(Account account) {
super(account);
}
@Override
public Account openAccount() {
System.out.println("打开农业银行账号");
account.openAccount();//桥接到指定账号的方法
return account;
}
}
public class Test {
public static void main(String[] args) {
Bank icbcBank = new ICBCBank(new SavingAccount());
Account icbcSavingAccount = icbcBank.openAccount();
icbcSavingAccount.showAccountType();
icbcBank = new ICBCBank(new DepositAccount());
Account icbcDepositAccount = icbcBank.openAccount();
icbcDepositAccount.showAccountType();
Bank abcBank = new ABCBank(new DepositAccount());
Account abcDepositAccount = abcBank.openAccount();
abcDepositAccount.showAccountType();
}
}
UML
1560061739132.png12.代理模式
定义:为其他对象提供一种代理,以控制对这个对象的访问。
核心:代理对象在客户端和目标对象之间起到中介的作用。
类型:结构型
适用场景:1)保护目标对象。2)增强目标对象
优点:1)代理模式能将代理对象与真是被调用的目标对象分离。2)一定程度上降低了系统的耦合度,扩展性好。3)保护目标对象。4)增强目标对象。
缺点:1)代理模式会造成系统设计中类的数目增加。2)在客户端和目标对象增加一个代理对象,会造成请求处理速度变慢。3)增加系统的复杂度。
①静态代理
public class User {
private String name;
private int age;
//省略get、set方法
}
public interface IUserDao {
int insert(User user);
}
public class UserDaoImpl implements IUserDao {
@Override
public int insert(User user) {
System.out.println("添加用户成功");
return 0;
}
}
public interface IUserService {
int addUser(User user);
}
public class UserServiceImpl implements IUserService {
private IUserDao iUserDao;
@Override
public int addUser(User user) {
System.out.println("service层通过调用dao层添加用户");
iUserDao = new UserDaoImpl();
int result = iUserDao.insert(user);
return result;
}
}
//静态代理类
public class UserServiceStaticProxy {
private IUserService iUserService;
public int addUser(User user){
beforeMethod();
iUserService = new UserServiceImpl();
iUserService.addUser(user);
afterMethod();
return 0;
}
private void beforeMethod(){
System.out.println("静态代理 before");
}
private void afterMethod(){
System.out.println("静态代理 after");
}
}
public class Test {
public static void main(String[] args) {
UserServiceStaticProxy userServiceStaticProxy = new UserServiceStaticProxy();
userServiceStaticProxy.addUser(new User());
}
}
UML图
1560064601995.png②动态代理
//动态代理类
public class UserServiceDynamicProxy implements InvocationHandler {
private Object target;
public UserServiceDynamicProxy(Object target) {
this.target = target;
}
public Object bind(){
Class clazz = target.getClass();
return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
beforeMethod();
Object result = method.invoke(target, args);
afterMethod();
return result;
}
private void beforeMethod(){
System.out.println("动态代理 beforeMethod");
}
private void afterMethod(){
System.out.println("动态代理 afterMethod");
}
}
public class Test {
public static void main(String[] args) {
IUserService userServiceDynamicProxy = (IUserService) new UserServiceDynamicProxy(new UserServiceImpl()).bind();
userServiceDynamicProxy.addUser(new User());
}
}
UML图
1560066129800.png13.模板方法模式
定义:定义了一个算法的骨架,并允许子类为一个或多个步骤提供实现。
核心:模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。
类型:行为型
适用场景:1)一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。2)各子类中公共的行为被提取出来并集中到一个公共父类中,从而避免代码重复。
优点:1)提高复用性。2)提高扩展性。3)符合开闭原则。
缺点:1)类数目增加。2)增加了系统实现的复杂度。3)继承关系自身缺点,如果父类添加新的抽象方法,所有子类都要改一遍。
业务描述:上线一个课程需要很多文件,例如:ppt、视频、手记、素材等。其中有些是必要的,有些是可以选择的。
//定义模板
public abstract class ACourse {
protected final void makeCourse(){
PPT();
Video();
if(needArticle()){
Article();
}
packageCourse();
}
//制作ppt就是制作ppt,没什么花样,所以不允许子类修改
final void PPT(){
System.out.println("制作PPT");
}
//制作视频就是制作视频,没什么花样,所以不允许子类修改
final void Video(){
System.out.println("制作视频");
}
//制作手记就是制作手记,没什么花样,所以不允许子类修改
final void Article(){
System.out.println("制作手记");
}
//钩子方法(可以判断是否需要制作手记)
protected boolean needArticle(){
return false;
}
abstract void packageCourse();
}
//java课程
public class JavaCourse extends ACourse {
@Override
void packageCourse() {
System.out.println("提供课程源代码");
}
}
//前端课程
public class PECourse extends ACourse {
private boolean needArticleFlag = false;
//设置是否需要手记
public void setNeedArticleFlag(boolean needArticleFlag) {
this.needArticleFlag = needArticleFlag;
}
@Override
void packageCourse() {
System.out.println("提供课程源代码");
System.out.println("提供素材");
}
@Override
protected boolean needArticle() {
return this.needArticleFlag;
}
}
//测试
public class Test {
public static void main(String[] args) {
System.out.println("Java课程--------");
ACourse javaCourse = new JavaCourse();
javaCourse.makeCourse();
System.out.println("Java课程结束--------");
System.out.println();
System.out.println("前端课程--------");
ACourse peCourse = new PECourse();
((PECourse) peCourse).setNeedArticleFlag(true);//需要手记
peCourse.makeCourse();
System.out.println("前端课程结束--------");
}
}
UML
1560068729443.png14.迭代器模式
定义:提供一种方法,顺序访问一个集合对象中的各个元素,而又不暴露该对象的内部表示。
类型:行为型
适用场景:1)访问一个集合对象的内容而无须暴露它的内部表示。2)为遍历不同的集合结构提供一个统一的接口。
优点:1)分离了集合对象的遍历行为。
缺点:1)类的个数成对增加。
已经有很多现成的迭代器,直接使用就好了。
15.策略模式
定义:定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化不会影响到使用算法的用户。
核心:大量处理if...else
类型:行为型
适用场景:1)系统有很多类,而他们的区别仅仅在于他们的行为不同。2)一个系统需要动态地在几种算法中选择一种。
优点:1)开闭原则。2)避免使用多重条件转移语句。3)提高算法的保密性和安全性。
缺点:1)客户端必须知道所有的策略类,并自行决定使用哪一个策略类。2)产生很多策略类。
//促销策略接口
public interface PromotionStrategy {
void doPromotion();
}
//返现促销
public class FanxianPromotionStrategy implements PromotionStrategy {
@Override
public void doPromotion() {
System.out.println("返现促销");
}
}
//满减促销
public class ManJianPromotionStrategy implements PromotionStrategy{
@Override
public void doPromotion() {
System.out.println("满减促销");
}
}
//立减促销
public class LiJianPromotionStrategy implements PromotionStrategy {
@Override
public void doPromotion() {
System.out.println("立减促销");
}
}
//策略模式,实际策略活动类
public class PromotionActivity {
private PromotionStrategy promotionStrategy;
public PromotionActivity(PromotionStrategy promotionStrategy) {
this.promotionStrategy = promotionStrategy;
}
public void executePromotion(){
promotionStrategy.doPromotion();
}
}
//测试类
public class Test {
public static void main(String[] args) {
PromotionActivity promotionActivity = new PromotionActivity(new ManJianPromotionStrategy());
promotionActivity.executePromotion();
promotionActivity = new PromotionActivity(new FanxianPromotionStrategy());
promotionActivity.executePromotion();
}
}
//这样做会实例化大量的类
UML
1560141370687.png//使用工厂模式改进
//使用工厂模式管理促销类
public class PromotionStrategyFactory {
private static Map<String, PromotionStrategy> PROMOTION_STRATEGY_MAP = new HashMap<>();
private static final EmptyPromotionStrategy EMPTY_PROMOTION_STRATEGY = new EmptyPromotionStrategy();
private interface PromotionKey{
String FANXIAN = "FANXIAN";
String LIJIAN = "LIJIAN";
String MANJIAN = "MANJIAN";
}
static{
PROMOTION_STRATEGY_MAP.put(PromotionKey.FANXIAN, new FanxianPromotionStrategy());
PROMOTION_STRATEGY_MAP.put(PromotionKey.LIJIAN, new LiJianPromotionStrategy());
PROMOTION_STRATEGY_MAP.put(PromotionKey.MANJIAN, new ManJianPromotionStrategy());
}
private PromotionStrategyFactory(){}
public static PromotionStrategy getPromotionStrategy(String promotionKey){
PromotionStrategy promotionStrategy = PROMOTION_STRATEGY_MAP.get(promotionKey);
return promotionStrategy == null ? EMPTY_PROMOTION_STRATEGY : promotionStrategy;
}
}
public class EmptyPromotionStrategy implements PromotionStrategy {
@Override
public void doPromotion() {
System.out.println("无促销");
}
}
public class Test {
public static void main(String[] args) {
String promotion = "LIJIANzz";
PromotionStrategy promotionStrategy = PromotionStrategyFactory.getPromotionStrategy(promotion);
promotionStrategy.doPromotion();
}
}
UML
1560142080870.png16.解释器模式
定义:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
核心:为了解释一种语言,而为语言创建的解释器。
类型:行为型
适用场景:1)某个特定类型问题发生频率足够高。
优点:1)语法由很多类表示,容易改变以及扩展此“语言”。
缺点:1)当语言法则数量太多时,增加了系统的复杂度。
//使用较少,无代码
17.观察者模式
定义:定义了对象之间的一对多依赖,让多个观察者对象同时监听某一个主题对象,当主题对象发生变化时,它的所有依赖者(观察者)都会收到通知并更新。
类型:行为型
使用场景:1)关联行为场景,建立一套触发机制。
优点:1)观察者与被观察者之间建立一个抽象的耦合。2)观察者模式支持广播通信。
缺点:1)观察者之间有过多的细节依赖,提高时间消耗及程度复杂度。2)使用要得当,要避免循环调用。
//被观察者继承Observable类
public class Course extends Observable {
private String courseName;
public Course(String courseName) {
this.courseName = courseName;
}
public String getCourseName() {
return courseName;
}
public void produceQuestion(Course course, Question question){
System.out.println(question.getUserName() + "在" + course.courseName + "提交了问题");
setChanged();
notifyObservers(question);
}
}
//观察者实现Observer接口
public class Teacher implements Observer {
private String teacherName;
public Teacher(String teacherName) {
this.teacherName = teacherName;
}
@Override
public void update(Observable o, Object arg) {
Course course = (Course) o;
Question question = (Question) arg;
System.out.println(teacherName + "老师" + course.getCourseName() + "的课程收到了一个" + question.getUserName() + "提交的问答:" + question.getQuestionContent());
}
}
public class Question {
private String userName;
private String questionContent;
//省略get、set方法
}
public class Test {
public static void main(String[] args) {
Course course = new Course("Java基础课");
Teacher teacher = new Teacher("cjw");
course.addObserver(teacher);
Question question = new Question();
question.setQuestionContent("java反射如何实现");
question.setUserName("kk");
course.produceQuestion(course, question);
}
}
UML
1560146159890.png18.备忘录模式
定义:保存一个对象的某个状态,以便在适当的时候恢复对象。
核心:就是“后悔药”。
类型:行为型
适用场景:1)保存及恢复数据相关业务场景。2)后悔的时候,即想恢复到之前的状态。
优点:1)为用户提供一种可恢复机制。2)存档信息的封装。
缺点:1)资源占用。
实际应用的不多,无代码,实现类似存档回退(使用栈)。
19.命令模式
定义:讲“请求”封装成对象,以便使用不同的请求。
核心:命令模式解决了应用程序中对象的职责以及它们之间的通信方式。
类型:行为型
使用场景:1)请求调用者和请求接收者需要解耦,使得调用者和接收者不直接交互。2)需要抽象出等待执行的行为。
优点:1)降低耦合。2)容易扩展新命令或一组命令。
缺点:1)命令的无限扩展会增加类的数量,提高系统实现复杂度。
描述:老板命令员工打开课程视频和关闭视频一个或多个命令。
public class CourseVideo {
private String name;
public CourseVideo(String name) {
this.name = name;
}
public void open(){
System.out.println(this.name + "打开视频");
}
public void close(){
System.out.println(this.name + "关闭视频");
}
}
public interface Command {
void execute();
}
public class OpenCourseVideoCommand implements Command {
private CourseVideo courseVideo;
public OpenCourseVideoCommand(CourseVideo courseVideo) {
this.courseVideo = courseVideo;
}
@Override
public void execute() {
courseVideo.open();
}
}
public class CloseCourseVideoCommand implements Command {
private CourseVideo courseVideo;
public CloseCourseVideoCommand(CourseVideo courseVideo) {
this.courseVideo = courseVideo;
}
@Override
public void execute() {
courseVideo.close();
}
}
//员工类
public class Staff {
private List<Command> commandList = new ArrayList<>();
public void add(Command command){
commandList.add(command);
}
public void execute(){
for (Command command : commandList) {
command.execute();
}
}
}
public class Test {
public static void main(String[] args) {
CourseVideo courseVideo = new CourseVideo("Java基础课程");
OpenCourseVideoCommand openCourseVideoCommand = new OpenCourseVideoCommand(courseVideo);
CloseCourseVideoCommand closeCourseVideoCommand = new CloseCourseVideoCommand(courseVideo);
Staff staff = new Staff();
staff.add(openCourseVideoCommand);
staff.add(closeCourseVideoCommand);
staff.add(openCourseVideoCommand);
staff.execute();
}
}
UML
1560148011699.png20.中介者模式
定义:定义一个封装一组对象如何交互的对象
核心:通过使对象明确地相互引用来促进松散耦合,并允许独立地改变它们的交互。
类型:行为型。
使用场景:1)系统中对象之间存在复杂的引用关系,产生的相互依赖关系结构混乱且难以理解。2)交互的公共行为,如果需要改变行为则可以增加新的中介者类。
优点:1)将一对多转化成了一对一、降低程序复杂度。2)类之间的解耦。
缺点:1)中介者过多,导致系统复杂。
//中介者
public class StudyGroup {
public static void showMessage(User user, String message){
System.out.println(new Date().toString() + "[" + user.getName() + "]:" + message);
}
}
//用户类
public class User {
private String name;
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//交由中介者负责转发信息
public void sendMessage(String message){
StudyGroup.showMessage(this, message);
}
}
//测试
public class Test {
public static void main(String[] args) {
User cjw = new User("cjw");
cjw.sendMessage("你们好!");
User kk = new User("kk");
kk.sendMessage("你好啊!");
}
}
UML
1560148677616.png21.责任链模式
定义:为请求创建一个接受此次请求对象的链。
类型:行为型。
使用场景:1)一个请求的处理需要多个对象当中的一个或几个协作处理。
优点:1)请求的发送者和接收者(请求的处理)解耦。2)责任链可以动态组合。
缺点:1)责任链太长或者处理时间过长,影响性能。2)责任链有可能过多。
描述:有一个课程需要上线,需要经过几个审核员,每个审核员审核的内容不同。
public class Course {
private String name;
private String video;
private String article;
//省略get、set方法
}
//审核员
public abstract class Approver {
protected Approver approver;//如果当前审核员审核通过,则交给下一个审核员
public void setApprover(Approver approver) {
this.approver = approver;
}
public abstract void deploy(Course course);
}
//审核视频
public class VideoApprover extends Approver {
@Override
public void deploy(Course course) {
if(StringUtils.isNotEmpty(course.getVideo())){
System.out.println(course.getName() + "含有手记,批准");
if(approver != null){
approver.deploy(course);
}
}else{
System.out.println(course.getName() + "不含有手记,不批准");
return;
}
}
}
//审核手记
public class ArticleApprover extends Approver {
@Override
public void deploy(Course course) {
if(StringUtils.isNotEmpty(course.getArticle())){
System.out.println(course.getName() + "含有视频,批准");
if(approver != null){
approver.deploy(course);
}
}else{
System.out.println(course.getName() + "不含有视频,不批准");
return;
}
}
}
//测试
public class Test {
public static void main(String[] args) {
Course course = new Course();
course.setName("Java基础课");
course.setArticle("Java基础课手记");
course.setVideo("Java基础课视频");
//先进行手记审计后视频
Approver articleApprover = new ArticleApprover();
Approver videoApprover = new VideoApprover();
articleApprover.setApprover(videoApprover);
articleApprover.deploy(course);
}
}
UML
1560150108870.png22.访问者模式
定义:封装作用于某种数据结构(如List、Map、Set等)中的各元素的操作。
核心:可以在不改变各元素的前提下,定义作用于这些元素的操作。
类型:行为型
使用场景:1)一个数据结构(如List、Map、Set等)包含很多类型的对象。2)数据结构与数据操作分离。
优点:1)增加新的操作很容易,即增加一个新的访问者。
缺点:1)增加新的数据结构困难。2)具体元素变更比较麻烦。
使用的比较少,无代码
23.状态模式
定义:允许一个对象在其内部状态改变时,改变它的行为。
类型:行为型
使用场景:1)一个对象存在多个状态(不同状态下行为不同),且状态可相互转换。
优点:1)将不同的状态隔离。2)把各种状态的转换逻辑,分布到State的子类中,减少相互间依赖。3)增加新的状态非常简单。
缺点:1)状态躲的业务场景导致类数目增加,系统变复杂。
描述:视频有暂停,快进,播放,停止。如果是停止的状态不无法快进的。需要转换。
//视频状态
public abstract class CourseVideoState {
protected CourseVideoContext courseVideoContext;//视频上下文
public void setCourseVideoContext(CourseVideoContext courseVideoContext) {
this.courseVideoContext = courseVideoContext;
}
public abstract void play();
public abstract void stop();
public abstract void pause();
public abstract void speed();
}
//上下文类
public class CourseVideoContext {
private CourseVideoState courseVideoState;//上下文状态
public final static PlayState PLAY_STATE = new PlayState();
public final static StopState STOP_STATE = new StopState();
public final static PauseState PAUSE_STATE = new PauseState();
public final static SpeedState SPEED_STATE = new SpeedState();
public CourseVideoState getCourseVideoState() {
return courseVideoState;
}
public void setCourseVideoState(CourseVideoState courseVideoState) {
this.courseVideoState = courseVideoState;
this.courseVideoState.setCourseVideoContext(this);//赋值课程视频视频的上下文
}
public void play(){
this.courseVideoState.play();
}
public void stop(){
this.courseVideoState.stop();
}
public void speed(){
this.courseVideoState.speed();
}
public void pause(){
this.courseVideoState.pause();
}
}
//播放状态
public class PlayState extends CourseVideoState {
@Override
public void play() {
System.out.println("视频正在播放状态");
}
//下面都是改变状态的操作
@Override
public void stop() {
super.courseVideoContext.setCourseVideoState(CourseVideoContext.STOP_STATE);
}
@Override
public void pause() {
super.courseVideoContext.setCourseVideoState(CourseVideoContext.PAUSE_STATE);
}
@Override
public void speed() {
super.courseVideoContext.setCourseVideoState(CourseVideoContext.SPEED_STATE);
}
}
//快进状态
public class SpeedState extends CourseVideoState {
@Override
public void play() {
super.courseVideoContext.setCourseVideoState(CourseVideoContext.PLAY_STATE);
}
@Override
public void stop() {
super.courseVideoContext.setCourseVideoState(CourseVideoContext.STOP_STATE);
}
@Override
public void pause() {
super.courseVideoContext.setCourseVideoState(CourseVideoContext.PAUSE_STATE);
}
@Override
public void speed() {
System.out.println("视频快进状态");
}
}
//暂停状态
public class PauseState extends CourseVideoState{
@Override
public void play() {
super.courseVideoContext.setCourseVideoState(CourseVideoContext.PLAY_STATE);
}
@Override
public void stop() {
super.courseVideoContext.setCourseVideoState(CourseVideoContext.STOP_STATE);
}
@Override
public void pause() {
System.out.println("视频暂停状态");
}
@Override
public void speed() {
super.courseVideoContext.setCourseVideoState(CourseVideoContext.SPEED_STATE);
}
}
//停止状态
public class StopState extends CourseVideoState {
@Override
public void play() {
super.courseVideoContext.setCourseVideoState(CourseVideoContext.PLAY_STATE);
}
@Override
public void stop() {
System.out.println("视频停止状态");
}
@Override
public void pause() {
System.out.println("ERROR 停止状态不能暂停");
}
@Override
public void speed() {
System.out.println("ERROR 停止状态不能快进");
}
}
//测试
public class Test {
public static void main(String[] args) {
CourseVideoContext courseVideoContext = new CourseVideoContext();
courseVideoContext.setCourseVideoState(new PlayState());
System.out.println("当前状态:" + courseVideoContext.getCourseVideoState().getClass().getSimpleName());
courseVideoContext.pause();
System.out.println("当前状态:" + courseVideoContext.getCourseVideoState().getClass().getSimpleName());
courseVideoContext.speed();
System.out.println("当前状态:" + courseVideoContext.getCourseVideoState().getClass().getSimpleName());
courseVideoContext.stop();
System.out.println("当前状态:" + courseVideoContext.getCourseVideoState().getClass().getSimpleName());
}
}
网友评论