● 请你说一下常用设计模式;
参考回答:
(1)单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点,避免一个全局使用的类频繁的创建和销毁,节省系统资源,提高程序效率。
实现方式:
将被实现的类的构造方法设计成private的。
添加此类引用的静态成员变量,并为其实例化。
在被实现的类中提供公共的Create Instance函数,返回实例化的此类,就是2中的静态成员变量。
单例模式只允许创建一个对象,因此节省内存,加快对象访问速度,适用场景:
需要频繁实例化然后销毁的对象。
创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
有状态的工具类对象。
频繁访问数据库或文件的对象。
场景举例:
每台计算机有若干个打印机,但只能有一个PrinterSpooler,以避免两个打印作业同时输出到打印机;
Windows的TaskManager(任务管理器),不能打开两个windows task manager;
Windows的Recycle Bin(回收站),在整个系统运行过程中,回收站一直维护着仅有的一个实例;
网站的计数器,一般也是采用单例模式实现,否则难以同步;
(2)策略模式:策略模式是把一个类中经常改变或者将来可能改变的部分提取出来作为一个接口,然后在类中包含这个对象的实例,这样类的实例在运行时就可以随意调用实现了这个接口的类的行为。
实现方式:
提供公共接口或抽象类,定义需要使用的策略方法。(策略抽象类)
多个实现的策略抽象类的实现类。(策略实现类)
环境类,对多个实现类的封装,提供接口类型的成员量,可以在客户端中切换。
客户端调用环境类进行不同策略的切换。
类图:
Strategy:策略接口,用来约束一系列具体的策略算法。Context使用这个接口来调用具体的策略,实现定义的策略。
ConcreteStrategy:具体的策略实现,也就是具体的算法实现。
Context:上下文,负责与具体的策略交互,通常上下文会持有一个真正的策略实现。
适用场景:
如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
一个系统需要动态地在几种算法中选择一种。
一个类定义了多种行为, 并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入它们各自的Strategy类中以代替这些条件语句。
(3)简单工厂模式:定义一个用于创建对象的接口或抽象类,让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类。
类图:
实现方式:
抽象产品类(Product),是所创建的所有对象的父类,负责描述所有实例所共有的公共接口
多个具体的产品类(Concrete Product),具体产品角色是创建目标,所有创建的对象都充当这个角色的某个具体类的实例。
工厂类(Creator),负责实现创建所有实例的内部逻辑
适用场景:
在任何需要生成复杂对象的地方,都可以使用工厂方法模式。
当需要系统有比较好的扩展性时,可以考虑工厂模式,不同的产品用不同的实现工厂来组装。
(4)装饰模式:允许向一个现有的对象添加新的功能,同时又不改变其结构,以在不使用创造更多子类的情况下,将对象的功能加以扩展。
类图:
实现方式:
抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。
具体构件(ConcreteComponent)角色:定义一个将要接收附加责任的类。
装饰(Decorator)角色:持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。
具体装饰(ConcreteDecorator)角色:负责给构件对象“贴上”附加的责任。
适用场景:
扩展一个类的功能。
动态增加功能,动态撤销。
(5)观察者模式:对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
类图:
抽象主题(Subject)角色:把所有对观察者对象的引用保存在一个集合中,每个抽象主题角色都可以有任意数量的观察者。抽象主题提供一个接口,可以增加和删除观察者角色。一般用一个抽象类和接口来实现。
抽象观察者(Observer)角色:为所有具体的观察者定义一个接口,在得到主题的通知时更新自己。
具体主题(ConcreteSubject)角色:在具体主题内部状态改变时,给所有登记过的观察者发出通知。具体主题角色通常用一个子类实现。
具体观察者(ConcreteObserver)角色:该角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。通常用一个子类实现。如果需要,具体观察者角色可以保存一个指向具体主题角色的引用。
适用场景:
当一个抽象模型有两个方面, 其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
当对一个对象的改变需要同时改变其它对象, 而不知道具体有多少对象有待改变。
当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之, 你不希望这些对象是紧密耦合的。
● 请你手写一下单例模式
参考回答:
1、饿汉模式
public class Singleton {private static Singleton instance = new Singleton();private Singleton (){}public static Singleton getInstance() {return instance;}}
这种方式在类加载时就完成了初始化,所以类加载较慢,但获取对象的速度快。这种方式基于类加载机制避免了多线程的同步问题,但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化instance显然没有达到懒加载的效果。
2、懒汉模式(线程不安全)
public class Singleton {private static Singleton instance;private Singleton (){}public static Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}}
懒汉模式申明了一个静态对象,在用户第一次调用时初始化,虽然节约了资源,但第一次加载时需要实例化,反映稍慢一些,而且在多线程不能正常工作。
3、懒汉模式(线程安全)
public class Singleton {private static Singleton instance;private Singleton (){}public static synchronized Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}}
这种写法能够在多线程中很好的工作,但是每次调用getInstance方法时都需要进行同步,造成不必要的同步开销,而且大部分时候我们是用不到同步的,所以不建议用这种模式。
4、双重检查模式 (DCL)
public class Singleton {private volatile static Singleton singleton;private Singleton (){}public static Singleton getInstance() {if (instance== null) {synchronized (Singleton.class) {if (instance== null) {instance= new Singleton();}}}return singleton;}}
这种写法在getSingleton方法中对singleton进行了两次判空,第一次是为了不必要的同步,第二次是在singleton等于null的情况下才创建实例。
● 请问设计模式是什么?
参考回答:
设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。
设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性。毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理地运用设计模式可以完美地解决很多问题,每种模式在现实中都有相应的原理来与之对应,每种模式都描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是设计模式能被广泛应用的原因。
● 请你手写一下单例模式代码
参考回答:
懒汉式单例模式:延迟实例化,但节省空间
package com.sxh.singleton;
public class SingleTon {
/*
* volatile关键字确保:当uniqueInstance变量被初始化为SingleTon实例时,多个线程能正确的处理uniqueInstance变量
* 分析:volatile修饰的成员变量,在每次被线程访问时,都强制性的从共享内存重读该成员的值;
* 当值发生变化是,强制线程将变化值写入共享内存,任何时候不同线程总是看到你某个成员变量的同一个值
* */
private volatile static SingleTon uniqueInstance;//利用一个静态变量来记录SingleTon类的唯一实例
//其他有用的单件类的数据
private SingleTon(){} //类外无法访问
public static SingleTon getInstance(){
/*
* 使用”双重检查加锁“,在getInstance中减少使用同步
* 首先检查是否实例已经创建了,如果尚未创建,才进行同步;只有第一次访问getInstance会同步
*/
if(uniqueInstance==null){ //确保只有一个实例
synchronized (SingleTon.class) { //多线程的情况不会出现问题,线程同步问题
if(uniqueInstance==null){
uniqueInstance=new SingleTon();//如果我们不需要这个实例,则永远不会产生
}
}
}
return uniqueInstance;
}
//其他有用的单件类的方法,单件类也可以是一般的类,具有一般的数据和方法
}
饿汉式单例模式:急切的创建实例,而不用延迟实例化
package com.sxh.singleton;
public class SingleTon {
private volatile static SingleTon uniqueInstance=new SingleTon();
//其他有用的单件类的数据
private SingleTon(){} //类外无法访问
public static SingleTon getInstance(){
return uniqueInstance;
}
//其他有用的单件类的方法,单件类也可以是一般的类,具有一般的数据和方法
}
IoDH实现单例模式
package com.sxh.singleton;
public class SingleTon {
private SingleTon(){} //类外无法访问
private static class HolderClass{ //静态内部类
private static final SingleTon uniqueinstance=new SingleTon();
}
public static SingleTon getInstance(){
return HolderClass.uniqueinstance;
}
//其他有用的单件类的方法,单件类也可以是一般的类,具有一般的数据和方法