设计模式
1、讲你最熟悉的设计模式、项目中使用的设计模式?
最熟悉的设计模式
-
工厂模式:工厂类可以根据条件生成不同的子类实例,这些子类有一个公共的抽象父类并且实现了相同的方法,但是这些方法针对不同的数据进行了不同的操作(多态方法)。当得到子类的实例后,开发人员可以调用基类中的方法而不必考虑到底返回的是哪一个子类的实例。
-
代理模式:给一个对象提供一个代理对象,并由代理对象控制原对象的引用。实际开发中,按照使用目的的不同,代理可以分为:远程代理、虚拟代理、保护代理、Cache代理、防火墙代理、同步化代理、智能引用代理。
-
适配器模式:把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起使用的类能够一起工作。
-
模板方法模式:提供一个抽象类,将部分逻辑以具体方法或构造器的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法(多态实现),从而实现不同的业务逻辑。
项目中使用的设计模式
- 策略模式:有一个商城项目,用户付款完成,修改用户积分操作,用户积分是通过多种方式产生的,比如第一次成功付款购物是双倍,用户等级如果是v1,积分是1倍,用户等级是v2,积分是1.5倍等,如果是生日当天购物,积分是三倍等,获取积分方式就可以用策略模式来完成。
- 单例设计模式:配置文件的信息和数据库中的数据,这些数据只有一份,并且多个线程共享,记录在DataCenter,DataCenter是一个单例类。
2、抽象类一般使用什么模式?
模版模式
3、模版方法一般使用什么场景?
Java IO InputStream,JDK自带的很多工具类都运用了某些设计模式,其中JavaIO中的InputStream这个抽象类中有3个实现了相类似功能的度read()方法。
- 第一个read()方法指定每次只读取1个字节
- 第二个read()方法指定将读取的二进制数据放在字节数组b中
- 第三个read()方法执行将读取的len个字节,放在字节数组b以off偏移量的起始位置处
4、接口一般使用什么模式?
工厂模式,代理模式
5、静态工厂和实例工厂的区别?
静态工厂方法
就是直接可以通过静态方法来实例化一个对象
public class HelloWorldFactory {
public static HelloWorld getInstance(){
return new HelloWorld();
}
}
HelloWorldFactory.getInstance()
创建 HelloWorld 对象直接可以调用getInstance方法来进行实现
实例工厂方法
就是先创建类对象,如何通过对象来调用创建实例对象的方法
public class HelloWorldFactory {
public HelloWorld createHelloWorld(){
return new HelloWorld();
}
}
HelloWorldFactory helloFactory = new HelloWorldFactory();
helloFactory.createHelloWorld();
抽象工厂模式
介绍
提供一个接口,用于创建相关的对象家族。
抽象工厂模式创建的是对象家族,也就是很多对象而不是一个对象,并且这些对象是相关的,也就是说必须一起创建出来。而工厂方法模式只是用于创建一个对象,这和抽象工厂模式有很大不同。
代码
JDK
- javax.xml.parsers.DocumentBuilderFactory
- javax.xml.transform.TransformerFactory
- javax.xml.xpath.XPathFactory
适配器模式
介绍
将一个类的接口变换成客户端所期待的另一个接口,从而使原本因接口不匹配而无法在一起工作的两个类功购在一起工作。
建造者模式
介绍
封装一个对象的构造过程,并允许按步骤构造。
JDK
- java.lang.StringBuilder
- java.nio.ByteBuffer
- java.lang.StringBuffer
- java.lang.Appendable
- Apache Camel builders
责任链模式
介绍
使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。
将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止.
JDK
- java.util.logging.Logger#log()
- Apache Commons Chain
- javax.servlet.Filter#doFilter()
命令模式
介绍
将命令封装成对象中,具有以下作用:
- 使用命令来参数化其它对象
- 将命令放入队列中进行排队
- 将命令的操作记录到日志中
- 支持可撤销的操作
JDK
- java.lang.Runnable
- Netflix Hystrix
- javax.swing.Action
组合模式
介绍
将对象组合成树形结构来表示"整体/部分"层次关系,允许用户以相同的方式处理单独对象和组合对象
JDK
- javax.swing.JComponent#add(Component)
- java.awt.Container#add(Component)
- java.util.Map#putAll(Map)
- java.util.List#addAll(Collection)
- java.util.Set#addAll(Collection)
装饰模式
介绍
动态地给一个对象添加一些额外的指责,就添加功能来说,装饰模式相比生成子类更为灵活
外观模式
介绍
提供了一个统一的接口,用来访问子系统中的一群接口,从而让子系统更容易使用。
工厂方法模式
介绍
定义了一个创建对象的接口,但由子类决定要实例化哪个类。工厂方法把实例化操作推迟到子类。
工厂方法模式设计到一下四个角色的概念:
- 抽象工厂角色
工厂方法模式的核心,创建产品的类必须实现该接口 - 具体工厂角色
该角色实现了抽象工厂接口,具体如何创建产品就是在该类中实现 - 抽象产品角色
所有产品的超类,负责实现产品共性的抽象定义 - 具体产品角色
该角色实现了抽象产品接口,负责具体不同产品的业务逻辑
JDK
- java.util.Calendar
- java.util.ResourceBundle
- java.text.NumberFormat
- java.nio.charset.Charset
- java.net.URLStreamHandlerFactory
- java.util.EnumSet
- javax.xml.bind.JAXBContext
建造者模式
介绍
它提供一种方法访问一个容器对象中各个元素,而又不需暴露该对象的内部细节
JDK
- java.util.Iterator
- java.util.Enumeration
中介者模式
介绍
集中相关对象之间复杂的沟通和控制方式。
用一个中介对象封装一系列的对象交互,中介者使各对象不需要显示地互相作用,从而使其耦合松散,而且可以独立地改变他们的交互
JDK
- All scheduleXXX() methods of java.util.Timer
- java.util.concurrent.Executor#execute()
- submit() and invokeXXX() methods of java.util.concurrent.ExecutorService
- scheduleXXX() methods of java.util.concurrent.ScheduledExecutorService
- java.lang.reflect.Method#invoke()
备忘录模式
介绍
在不违背封装的情况下获取对象的内部状态,从而在需要时可以将对象恢复到最初状态
- Originator:原始对象
- Caretaker:负责保存好备忘录
- Menento:备忘录,存储原始对象的的状态。备忘录实际上有两个接口,一个是提供给 Caretaker 的窄接口:它只能将备忘录传递给其它对象;一个是提供给 Originator 的宽接口,允许它访问到先前状态所需的所有数据。理想情况是只允许 Originator 访问本备忘录的内部状态
JDK
- java.io.Serializable
原型模式
介绍
使用原型实例指定要创建对象的类型,通过复制这个原型来创建新对象。
JDK
- java.lang.Object#clone()
代理模式
介绍
为其他对象提供一种代理以控制对这个对象的访问
代理模式一般涉及到的角色有:
抽象角色:声明真实对象和代理对象的共同接口;
代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。
真实角色:代理角色所代表的真实对象,是我们最终要引用的对象。
JDK
- java.lang.reflect.Proxy
- RMI
单例模式
介绍
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
代码
public class Singleton {
private Singleton() {
}
private static class SingletonInstance {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}
优点
- 单例模式内存只有一个实例,减少了内存开销,当需要频繁创建和销毁的对象时单例模式无疑可以提高系统的性能。
- 避免对资源的多重占用
- 单例模式可以在系统设置全局的访问点,优化和共享资源访问
缺点
- 由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。
- 在并行开发环境,如果单例模式没有完成,是不能进行测试。
- 单例类的职责过重,在一定程度上违背了“单一职责原则”。
- 滥用单例将带来一些负面的问题,如为了节省资源将数据库连接池对象设计为单例模式,可能会导致共享连接池对象的程序过多未出而出现的连接池溢出,如果实例化对象长时间不用系统就会被认为垃圾对象被回收,这将导致对象状态丢失。
应用场景
- 外部资源:每台计算机有若干个打印机,但只能有一个PrinterSpooler,以避免两个打印作业同时输出到打印机。内部资源:大多数软件都有一个(或多个)属性文件存放系统配置,这样的系统应该有一个对象管理这些属性文件
- Windows的Task Manager(任务管理器)就是很典型的单例模式(这个很熟悉吧),想想看,是不是呢,你能打开两个windows task manager吗? 不信你自己试试看哦~
- windows的Recycle Bin(回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。
- 网站的计数器,一般也是采用单例模式实现,否则难以同步。
- 应用程序的日志应用,一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。
- Web应用的配置对象的读取,一般也应用单例模式,这个是由于配置文件是共享的资源。
- 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。数据库软件系统中使用数据库连接池,主要是节省打开或者关闭数据库连接所引起的效率损耗,这种效率上的损耗还是非常昂贵的,因为何用单例模式来维护,就可以大大降低这种损耗。
- 多线程的线程池的设计一般也是采用单例模式,这是由于线程池要方便对池中的线程进行控制。
- 操作系统的文件系统,也是大的单例模式实现的具体例子,一个操作系统只能有一个文件系统。
- HttpApplication 也是单位例的典型应用。熟悉ASP.Net(IIS)的整个请求生命周期的人应该知道HttpApplication也是单例模式,所有的HttpModule都共享一个HttpApplication实例.
策略模式
介绍
定义一系列算法,封装每个算法,并使它们可以互换。
策略模式可以让算法独立于使用它的客户端。
JDK
- java.util.Comparator#compare()
- javax.servlet.http.HttpServlet
- javax.servlet.Filter#doFilter()
模版方法模式
介绍
定义算法框架,并将一些步骤的实现延迟到子类。
通过模板方法,子类可以重新定义算法的某些步骤,而不用改变算法的结构。
JDK
- java.util.Collections#sort()
- java.io.InputStream#skip()
- java.io.InputStream#read()
- java.util.AbstractList#indexOf()