常见的设计模式
单例模式
在某些情况下,整个程序运行过程中只允许出现类的一个实例,但又经常频繁销毁和创建的对象,尤其是一些重量级对象,此时我们只需要创建一个对象实例,比如全局缓存,浏览器中的window对象等,单例模式只保证一个类仅有一个实例,并提供它的全局访问方法。
饿汉模式
饿汉模式,即目标对象在不使用的时候就创建出来
class HungrySingleton{
private static HungrySingleton instance=new HungrySingleton();
private HungrySingleton(){}
public HungrySingleton getInstance(){
return instance;
}
}
懒汉模式
懒汉模式,即目标对象在使用的时候才会被创建出来,但是在并发环境下会出现线程安全问题
class LazySingleton{
private static LazySingleton instance;
private LazySingleton(){}
public LazySingleton getInstance(){
if(instance==null){
instance=new LazySingleton();
}
return instance;
}
}
懒汉模式+synchronized
由于懒汉模式在并发环境下会有线程安全的问题,以此可以在创建对象前需要获得排他锁,才能允许进入临界区
class LazySingleton{
private static LazySingleton instance;
private LazySingleton(){}
public synchronized LazySingleton getInstance(){
if(instance==null){
instance=new LazySingleton();
}
return instance;
}
}
懒汉模式+双重校验模式
由于我们只需在目标对象为空时,才需要加锁然后进去获取实例对象,而不是目标对象是否已经实例了都要加锁,所以可以缩小加锁范围,即锁细粒化
class DoubleChechSingleton{
private static DoubleChechSingleton instance;
private DoubleChechSingleton(){}
public DoubleChechSingleton getInstance(){
if(instance==null){
synchronized(DoubleChechSingleton.class){
if(instance==null){//再次判断是因为可能在获取锁的时候,已经被别对线程实例化了。
instance=new DoubleChechSingleton();
}
}
}
return instance;
}
}
工厂模式
- 工厂模式的目的是为了实现解耦,将对象的创建和使用分开,即应用程序将对象的创建和初始化职责交给工厂对象。若一个对象A想要调用对象B时,如果直接通过new关键字来创建一个B实例,然后调用B实例,这样做的不好处是,当需求变更,要将B实例换成C实例时,则需要修改所有new了该实例的方法。
- 降低代码重复。如果对象B的创建过程比较复杂,并且很多地方都用到了,那么很可能出现很多重复的代码,通过统一将创建对象B的代码放到工厂里面统一管理,可以减少代码的重复率,同时也方便维护。相比于构造函数来说,复杂的初始化,会使得构造函数非常的复杂。由于创建过程都由工厂统一的管理,有利于当业务发生变化之后的修改。
- 工厂模式将创建和使用分离,使用者不需要知道具体的创建过程,只需要使用即可
简单工厂模式
简单工厂模式就是通过判断来选择要创建的对象
interface Phone{//目标类需要实现的接口
public void createPhone();
}
class XiaomiPhone implements Phone{//小米手机
public void createPhone(){
System.out.println("生产小米手机");
}
}
class HuaweiPhone implements Phone{//华为手机
public void createPhone(){
System.out.println("生产华为手机");
}
}
class PhoneFactory{//工厂类
public Phone getPhone(String type){//通过传入的参数来判断需要创建那个类
if(type.equals("小米")){
return new XiaomiPhone();
}else if(type.equals("华为")){
return new HuaweiPhone();
}else{
return null;
}
}
}
工厂方法
工厂方法就是给每个目标类创建一个工厂类,然后通过获取需要的目标类的工厂类来获取目标对象
interface Phone{//目标类需要实现的接口
public void createPhone();
}
class XiaomiPhone implements Phone{//小米手机
public void createPhone(){
System.out.println("生产小米手机");
}
}
class HuaweiPhone implements Phone{//华为手机
public void createPhone(){
System.out.println("生产华为手机");
}
}
interface Factory{//目标类的工厂类需要实现的接口
public Phone getPhone();
}
class XiaomiFactory implements Factory{//小米类工厂
@Override
public Phone getPhone() {
return new XiaomiPhone();
}
}
class HuaweiFactory implements Factory{//华为类工厂
@Override
public Phone getPhone() {
return new HuaweiPhone();
}
}
class Main{
public static void main(String[] args){
Factory factory=new XiaomiFactory();//获取小米工厂
Phone=factory.getPhone();//获取小米手机
}
}
适配器模式
适配器模式就是将一个类的接口转换成使用者希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作
类适配器模式
在适配器中的新方法中调用原实现类中的方法,此方式耦合度较高
class Source{//需要适配的类
public Source(){}
public void oldMethod(){}
}
interface Target{//适配器要实现的接口
void newMethod();
}
class Adapter extends Source implements Target{//适配器
public Adapter(){
super();
}
@Override
public void newMethod() {//新的接口方法
//do something;
this.oldMethod();
//do something;
}
}
对象适配器模式
由于类适配器模式的耦合性比较高,因此可以将原实现类的实例传给适配器,适配器在目标接口中调用该实例的方法
class Source{//需要适配的类
public Source(){}
public void oldMethod(){}
}
interface Target{//适配器要实现的接口
void newMethod();
}
class Adapter implements Target{//适配器
private Source source;
public Adapter(Source source){//传入要适配的对象
this.source=source;
}
@Override
public void newMethod() {//新的接口方法
//do something;
source.oldMethod();
//do something;
}
}
装饰器模式
装饰器模通过创建一个装饰器类来包装一个现有类从而为这个现有类添加新的功能,同时又不改变其结构。如果采用类继承的方式,会出现耦合度高,并且随着扩展功能的增多,子类会膨胀,或者这些现有类是带有final关键字的类使得不能通过继承方式来为现有类添加功能。但是釆用装饰器模式来可以实现对现有类的扩展。
主要包括的角色有:
- 抽象构件:定义一个抽象结构以规范准备接受附加责任的对象。
- 具体构件:实现抽象构件,通过装饰角色为其添加一些责任。
- 抽象装饰:继承抽象构件,并且包含具体构件的实例,可以通过其子类扩展具体构件的功能。
- 具体装饰:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任
//需要被添加新功能的类要实现的接口
interface AbstractComponent{
public void operation();
}
//需要被添加新功能的类
class ConcreteComponent implements AbstractComponent{
public ConcreteComponent(){}
@Override
public void operation() {}
}
//抽象装饰类
class AbstractDecorator implements AbstractComponent{
private AbstractComponent abstractComponent;
public AbstractDecorator(AbstractComponent abstractComponent){
this.abstractComponent=abstractComponent;
}
@Override
public void operation() {
abstractComponent.operation();
}
}
//具体装饰类
class ConcreteDecorator extends AbstractDecorator{
public ConcreteDecorator(AbstractDecorator abstractDecorator){
super(abstractDecorator);
}
@Override
public void operation(){
super.operation();
addedFunction();//新添加的功能
}
public void addedFunction(){}
}
代理模式
代理模式;当不能直接访问某个目标对象,需要一个中介作为代理对象来帮助访问对象完成其访问目标对象的任务,在一定程度上降低了系统耦合度。
静态代理模式
由程序员创建代理类或者特定工具自动生成源代码对其编译,在程序运行前代理类的.class文件就已经存在了。
interface Subject{//目标类需要实现的接口
public void request();
}
class RealSubject implements Subject{//目标类
@Override
public void request(){}
}
class Proxy implements Subject{//代理类
private Subject subject;
public Proxy(Subject subject){
this.subject=subject;
}
public void request(){
preRequest();//添加的其他功能
subject.request();
postRequest();//添加的其他功能
}
public void preRequest(){}
public void postRequest(){}
}
动态代理模式
利用反射机制在运行时创建代理类
- 基于JDK的动态代理,这种方式需要目标类实现接口
interface Subject{//目标接口
public void request();
}
class RealSubject implements Subject{//目标类
@Override
public void request(){}
}
class ProxyHandler implements InvocationHandler{//代理类
private Subject subject;
public ProxyHandler(Subject subject){
this.subject=subject;
}
@Override
public Object invoke(Object proxy,Method method,Object[] args)throws Throwable{
if(method.getName()=="request"){
preRequest();
method.invoke(subject,args);
postRequest();
return null;
}
return null;
}
public void preRequest(){}
public void postRequest(){}
}
class Main{
public static void main(String[] args){
Subject subject=new RealSubject();
InvocationHandler proxyHandler=new ProxyHandler(subject);
Subject proxyInstance=(Subject)Proxy.newProxyInstance(proxyHandler.getClass()
.getClassLoader(),subject.getClass().getInterfaces(),proxyHandler);
proxyInstance.request();
}
}
- 基于CGLIB的动态代理,目标类可以不同实现接口
public class RealSubject{//目标类
public RealSubject(){}
public void request(){}
}
class ProxyInterceptor implements MethodInterceptor{//代理类
@Override
public Object intercept(Object object,Method method,Object[] args,MethodProxy methodProxy)throws Throwable{
if(method.getName().equals("request")){
preRequest();
methodProxy.invokeSuper(object,args);
postRequest();
}
return null;
}
public void preReuqest(){}
public void postRequest(){}
}
class Main{
public static void main(String[] args){
Enhancer enhancer=new Enhancer();
enhancer.setSuperclass(RealSubject.class);
enhancer.setCallback(new ProxyInterceptor());
RealSubject realSubject=(RealSubject)enhancer.create();
realSubject.request();
}
}
不变模式
不变模式解决的是当多线程对一个对象的读操作,去除对对象的同步操作,依靠对象的不变性,不仅确保了对象内部在多线程环境下的一致性和正确性,也提高系统并发能力。它的核心思想是一个对象一旦被创建了,则它的内部状态就永远不会发生改变,即不会有线程可以去修改对象的内部,对象自身也不会发生改变。不变模式使用的场景需要满足:
- 对象创建后,内部状态和数据不再发生任何改变
- 对象需要被共享,被多线程频繁访问。
- 在构件一个不变模式的类时需要注意
- 去除所有setter()方法和所有修改自身属性的方法。
- 所有属性设置为私有和final。
- 确保没有子类可以重载修改它,即类是final的
- 有一个创建完整对象的构造函数
final class Product{//不变类
private final String no;
private final String name;
private final double price;
public Product(String no,String name, double price){
this.name=name;
this.no=no;
this.price=price;
}
public String getName(){return this.name;}
public String getNo(){return this.no;}
public double getPrice(){return this.price;}
}