• 从设计模式的角度看Java程序优化


    一、前言

      Java程序优化有很多种渠道,比如jvm优化、数据库优化等等,但都是亡羊补牢的措施,如果能在设计程序架构时利用设计模式就把程序的短板解决,就能使程序更加健壮切容易维护迭代

    二、常用的设计模式

    1、单例模式

      单例模式可以确保一个类只产生一个实例,对于系统中频繁使用的对象建议使用单例模式设计,可以节省创建对象耗费的时间以及减少GC清理的压力,代码如下

    public class A
    {
        // 实例化一个私有的静态对象
        private static A a = new A();
        // 构造方法私有,防止其他代码对其实例化
        private A()
        {
        }
        // 通过getInstance获取使用该对象
        public static A getInstance()
        {
            return a;
        }
    }

    因为对象实例是静态对象,所以JVM在加载这个类时就会把对象创建出来,这样无法做到延时加载(就是用到的时候再加载),所以还有一些写法会将对象设置为null,当有人调用getInstance方法时,先判断对象是否为空,为空再实例化对象,达到延时加载的目的,代码如下

    public class A
    {
        // 声明对象时赋值为null,加载类时,不会生成对象
        private static A a = null;
        private A()
        {
        }
        // 加入synchronized关键字,将这个办法锁起来,当多线程调用时竞争锁,达到线程安全
        public static synchronized A getInstance()
        {
            if (a == null)
            {
                a = new A();
            }
            return a;
        }
    }

    以上代码虽然解决了线程安全的问题,但是加入同步锁,会使代码效率降低为代价,所以还需要再改进一下,完美的做法如下

    public class A
    {
        private A()
        {
        }
        // 声明一个静态内部类
        private static class AHolder
        {
            private static A a = new A();
        }
        public static A getInstance()
        {
            // 直接返回内部类的对象
            return AHolder.a;
        }
    }

    以上代码声明了一个静态内部类,当调用A类时,内部类是不会被初始化的,当调用getInstance方法时,内部类才会被调用,内部类中的对象就会随之实例化,这样使用内部类巧妙的达成了延时加载的目的,同时也不用加锁,性能不会降低

    2、代理模式

       使用代理模式可以使用一个代理类来间接调用其他类的功能,达到屏蔽主类的效果,在程序优化的角度上,他可以做到:当系统启动时,会做许多初始化的操作,导致系统启动时非常缓慢,可以使用代理类来处理初始化的操作,当系统启动时,加载代理类,而不用做真正的初始化操作,当这个组件真正被用到时,代理类才会真正去调用初始化的操作,这样达到了系统压力的分摊,而加快了系统的启动速度。当然代理类除了应对性能问题,还有很多其他的用途。代码如下

    public interface IOperation
    {
        // 业务逻辑接口
        void doIt();
    }
    public class TrueOperation implements IOperation
    {
        @Override
        public void doIt()
        {
            // 真正的业务逻辑
            System.out.println("do it.");
        }
    }
    public class ProxyOperation implements IOperation
    {
        // 聚合真正的实现类
        private TrueOperation trueOperation = null;
    
        @Override
        public void doIt()
        {
            if (trueOperation == null)
            {
                trueOperation = new TrueOperation();
            }
            // 代理执行真正的方法
            trueOperation.doIt();
        }
    }

    以上代码可以看出,其实代理类就是将真正的业务类聚合到自己,然后代执行,只有当业务方法真正被调用时,业务类才会被初始化,也是延时加载的思想,以上是静态代理模式的写法,存在的问题就是,当业务多了以后,同时还要维护代理类,这样就增加了开发维护的难度,所以进一步优化就是使用动态代理模式,如下代码使用JDK自带的动态代理模式

    // 业务逻辑接口
    public interface IOperation
    {
        void operationA();
        void operationB();
    }
    // 业务逻辑的实现
    public class OperationImpl implements IOperation
    {
        @Override
        public void operationA()
        {
            System.out.println("do A...");
        }
        @Override
        public void operationB()
        {
            System.out.println("do B...");
        }
    }
    // 动态代理实现类
    public class OperationHandler implements InvocationHandler
    {
        // 需要代理的业务逻辑
        private OperationImpl operation = null;
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
        {
            // 延时加载
            if (operation == null)
            {
                operation = new OperationImpl();
            }
            // 执行一些自定义的逻辑
            System.out.println("before work...");
            // 代理执行业务逻辑
            method.invoke(operation, args);
            // 执行一些自定义的逻辑
            System.out.println("after work...");
            return null;
        }
    }
    public class Main
    {
        public static void main(String[] args)
        {
            // 生成代理对象
            IOperation operation = (IOperation)Proxy
                    .newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{IOperation.class}, new OperationHandler());
            // 代理执行业务
            operation.operationA();
            operation.operationB();
        }
    }

    如果需要更好性能的动态代理模式,可以使用第三方库,比如CGLIB、Javassist等

    3、享元模式

      当系统中存在多个相同的对象,可以优化让大家都是用同一个对象,工厂类中拿着一个对象实例,当需要使用到这个对象时,通过工厂类获取对象实例,不用每次使用都创建一遍对象,从而减少创建开销、GC压力和对象的内存占用。这听起来有点像单例模式和对象池的合体,这与对象池的区别就是:对象池中的每个对象都是等价的,而享元模式中的工厂的对象,则各有各自的用途,如下代码

    // 业务逻辑接口
    public interface IOperation
    {
        void doIt();
    }
    // 业务逻辑的实现
    public class OperationImplA implements IOperation
    {
        private String id = null;
        public OperationImplA(String id)
        {
            this.id = id;
        }
    
        @Override
        public void doIt()
        {
            System.out.println("do it A...");
        }
    }
    // 业务逻辑的实现
    public class OperationImplB implements IOperation
    {
        private String id = null;
        public OperationImplB(String id)
        {
            this.id = id;
        }
    
        @Override
        public void doIt()
        {
            System.out.println("do it B...");
        }
    }
    // 享元工厂
    public class OperationFactory
    {
        // 相同ID公用同一个对象
        private Map<String, IOperation> operationMapA = new HashMap<>();
        private Map<String, IOperation> operationMapB = new HashMap<>();
    
        public IOperation getOperationA(String id)
        {
            IOperation operation = operationMapA.get(id);
            // 延时加载
            if (operation == null)
            {
                operation = new OperationImplA(id);
                operationMapA.put(id, operation);
            }
            return operation;
        }
    
        public IOperation getOperationB(String id)
        {
            IOperation operation = operationMapB.get(id);
            // 延时加载
            if (operation == null)
            {
                operation = new OperationImplB(id);
                operationMapB.put(id, operation);
            }
            return operation;
        }
    }
    public class Main
    {
        public static void main(String[] args)
        {
            OperationFactory operationFactory = new OperationFactory();
            IOperation operationA = operationFactory.getOperationA("A");
            IOperation operationB = operationFactory.getOperationB("B");
            operationA.doIt();
            operationB.doIt();
        }
    }

    如上代码看出,每个id会共享一个对象,多次使用时可以复用对象

    4、装饰者模式

       装饰者模式也是比较常见的模式,他可以将系统中的功能组件进行包装,提升功能增加的能力,对于一些功能组件,我们可以用装饰者模式为其提高性能能力,具有低耦合的特性,最典型的例子就是JDK中的文件操作,平常经常使用FileInputStream来读取文件内容,FileInputStream是基于字节流的输入,不具备缓冲区的功能,JDK就使用装饰者模式将FileInputStream包装增加了缓冲区的性能优化,即BufferedInputStream,这个例子就是增加了功能组件的性能,还有例子比如Collections.synchronizedMap(),这个对HashMap进行了包装,增加了线程安全的特性。下面看一个简易实现代码

    // 业务逻辑接口
    public interface IOperation
    {
        void doIt();
    }
    // 业务逻辑的实现
    public class OperationImpl implements IOperation
    {
        @Override
        public void doIt()
        {
            System.out.println("do it...");
        }
    }
    public abstract class AbsOperation implements IOperation
    {
        IOperation operation = null;
        public AbsOperation(IOperation operation)
        {
            this.operation = operation;
        }
    }
    public class OperationPlusA extends AbsOperation
    {
        public OperationPlusA(IOperation operation)
        {
            super(operation);
        }
    
        @Override
        public void doIt()
        {
            System.out.println("增强功能A。。。");
            operation.doIt();
        }
    }
    public class OperationPlusB extends AbsOperation
    {
        public OperationPlusB(IOperation operation)
        {
            super(operation);
        }
    
        @Override
        public void doIt()
        {
            System.out.println("增强功能B。。。");
            operation.doIt();
        }
    }
    public class Main
    {
        public static void main(String[] args)
        {
            IOperation operation = new OperationPlusA(new OperationPlusB(new OperationImpl()));
            operation.doIt();
        }
    }

    以上代码可以看出,我们对功能组件进行了两次增强,每次增强都是互相独立的,不会互相影响

    5、观察者模式

      观察者模式对于性能优化主要在“通知”,当程序A依赖于程序B的执行,如果程序A开线程去轮询程序B来监视程序B的状态,那么会增加系统的负担,所以使用观察者模式来优化达成“通知”这个目的,当程序B执行完毕,就会回调程序A的方法,来告诉程序A自己已经执行好了,免去了开线程去监视的负担,JDK已经帮我们提供了观察者模式的接口,下面使用简单的代码去尝试一下观察者模式

    // 观察者
    public class ObserverA implements Observer
    {
        @Override
        public void update(Observable o, Object arg)
        {
            // 观察者的通知回调
            System.out.println("收到。。。");
        }
    }
    // 被观察者
    public class ObservableA extends Observable
    {
        @Override
        protected synchronized void setChanged()
        {
            // 改变状态方法
            super.setChanged();
        }
    }
    public class Main
    {
        public static void main(String[] args)
        {
            // 实例化观察者和被观察者
            ObserverA observerA = new ObserverA();
            ObservableA observableA = new ObservableA();
    
            // 将观察者加入到被观察者的观察列表中
            observableA.addObserver(observerA);
    
            // 改变状态,然后通知观察者自己改变了
            observableA.setChanged();
            observableA.notifyObservers();
            observableA.setChanged();
            observableA.notifyObservers();
        }
    }

    观察者需要重写update()方法来定义被通知时要做的事,而被观察者需要重写setChanged方法来改变自己的状态,使用时,先将观察者注册到被观察者中,需要通知时,要调用setChanged()方法改变状态,再调用notifyObservers()方法通知观察者,如果不改变状态是无法发出通知的

  • 相关阅读:
    第二周:If判断语句程序当中的作用简介
    普通B/S架构模式同步请求与AJAX异步请求区别(个人理解)
    第一周:Java基础知识总结(1)
    silverlight 碰撞检测
    超强silverlight绘图
    javascript 判断一个对象是否具有指定名称的属性
    关于IE的RegExp.exec
    浏览器 禁止选择
    silverlight 无限制坐标系统
    CSS Sprite样式生成器网页制作缺她不可
  • 原文地址:https://www.cnblogs.com/orange911/p/10439022.html
Copyright © 2020-2023  润新知