• java设计模式基础


    、单例模式(Singleton)

    1、单例对象(Singleton)是一种常用的设计模式.在Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在.这样的模式有几个好处:

      1>某些类创建比较频繁,对于一些大型的对象,这是一笔很大的系统开销.

      2>省去了new操作符,降低了系统内存的使用频率,减轻GC压力.

      3>有些类如交易所的核心交易引擎,控制着交易流程,如果该类可以创建多个的话,系统完全乱了.(比如一个军队出现了多个司令员同时指挥,肯定会乱成一团),所以只有使用单例模式,才能保证核心交易服务器独立控制整个流程.

    2、单例三步骤: 1.构造函数私有化2.类中创建一个本类对象3.提供一个方法可以获取该对象.

    3、对于该类的事物该怎么描述还是怎么描述,当需要内存中该类对象唯一时就加上面三步.

    4、懒汉式和饿汉式,延迟加载,实际中使用饿汉式,懒汉式有线程问题 (在判断是否为空时cpu切换,可以加sync..锁定,但是效率低).

    //饿汉式
    class Single{
        private static final Single s = new Single();
        private Single(){}
        public static Single getInstance(){
            return s;
        }
    }
    
    //懒汉式
    class Ehanshi(){
      private ehanshi(){} Ehanshi e = null;
      public static Ehanshi getInstance(){
        if(e==null){
          --》此处切换cpu,会有线程问题 
          e = new Ehanshi();
        }
      }
    }
    
    //解决方式一(效率低,每次需要判断是否锁定)
    class Ehanshi(){
      private ehanshi(){} Ehanshi e = null;
      public static Ehanshi synchronized getInstance(){
        if(e==null){ e = new Ehanshi();}
      }
    }
    //解决方式二 ,效率高 (但是还是建议使用饿汉式简单)
    class Ehanshi(){
      private ehanshi(){} Ehanshi e = null;
      public static Ehanshi getInstance(){
        if(e==null){
          synchronized(Ehanshi.class){
            if(e==null){
              e = new Ehanshi();
            }
          }
        }
      }
    }

    5、使用案例:

    例1:链接池可以使用单例模式,初始化的时候创建譬如100个connection对象,然后再需要的时候提供一个,用过之后返回到pool中,单例模式保证连接池有且只有一个.

    例2:Spring 的 IoC 容器在默认情况下对所有托管对象都是进行了单例化处理的,scope可以配置单例还是多例.

    例3:我们有时候项目简单的时候不想用spring,可以自己写工厂,工厂类做成单例.

    例4:web服务器中的token随机数生成器(给表单设置一个唯一 id)也是单例.

    ps:总之就是只需要用一个实例时,都可以使用单例.

    二、工厂模式

    建立一个工厂类,对实现了同一接口的一些类进行实例的创建,减少代码中大量的new,解耦合.spring的bean工厂,典型的大大的工厂模式.

    //单例,工厂
    public class ServiceFactory {
        
        private Properties serviceConfig = new Properties();
        //单例的构造函数也只执行一次
        private ServiceFactory(){
            InputStream in = DaoFactory.class.getClassLoader().getResourceAsStream("service.properties");
            try {
                serviceConfig.load(in);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        private static ServiceFactory instance = new ServiceFactory();
        public static ServiceFactory getInstance(){
            return instance;
        }
        public <T> T createService(Class<T> clazz){
            //clazz.getName()拿到的带包名
            String name = clazz.getSimpleName();
            String className = serviceConfig.getProperty(name);
            try {
                T service = (T) Class.forName(className).newInstance();
                return service;
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }
    //service.properties
    BusinessService=cn.itcast.service.impl.BusinessServiceImpl
    BusinessService service = (BusinessService) ServiceFactory.getInstance().createService(BusinessService.class);

    三、适配器模式(Adapter)

    将一个类的接口转换成客户期望的另一个接口,让原本不兼容的接口可以合作无间.比如去德国手机充电问题.

    http://www.cnblogs.com/wangjq/archive/2012/07/09/2582485.html 这个讲的比较好.

    四、装饰模式(Decorator)

    1、装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例.

    2、装饰器模式的应用场景:

      1>需要扩展一个类的功能.

      2>动态的为一个对象增加功能,而且还能动态撤销.(继承不能做到这一点,继承的功能是静态的,不能动态增删.)

    public interface Sourceable {
        public void method();
    }
    
    public class Source implements Sourceable {
        public void method() {
            System.out.println("the original method!");
        }
    }
    
    public class Decorator implements Sourceable {
    
        private Sourceable source;
    
        public Decorator(Sourceable source) {
            this.source = source;
        }
    
        public void method() {
            System.out.println("before decorator!");
            source.method();
            System.out.println("after decorator!");
        }
    }
    
    public class DecoratorTest {
    
        public static void main(String[] args) {
            Sourceable source = new Source();
            Sourceable obj = new Decorator(source);
            obj.method();
        }
    }

    ps:参见javaEE(12)_数据库连接池的第一种实现,目标是拦截close方法.其实给用户的数据库连接是经过我包装后的连接对象,感觉装饰模式可以被动态代理取代.

    五、动态代理模式

    1、代理模式的应用场景:如果已有的方法在使用的时候需要对原有的方法进行改进,和装饰模式要实现的目的一样,但是更简便,不拦截的方法无需一一写出来.

    2、代理对象存在的价值:用于拦截对真实业务方法的访问,如在jdbc数据库连接池中使用动态代理拦截close方法,将连接放入连接池而不是关闭.

    3、原理就是:实际用户拿到的连接是代理对象而不是正在的Connection对象,代理类实现InvocationHandler类.

    http://www.cnblogs.com/jqyp/archive/2010/08/20/1805041.html

    动态代理应用:

    //1、jdbc数据库连接池中使用动态代理拦截close方法,将连接放入连接池而不是关闭:
    public synchronized Connection getConnection() throws SQLException {
        if(list.size()<=0){
            throw new RuntimeException("数据库忙,请稍会再来!!");
        }
        Connection conn = list.removeFirst();   //list.get()不行
        return new MyConnectionHandler(conn,this).getWarpConn();
    }
    
    class MyConnectionHandler implements InvocationHandler {
        private Connection realConnection;
        private Connection warpedConnection;
        private MyDataSource dataSource;
    
        private int maxUseCount = 5;
        private int currentUserCount = 0;
    
        MyConnectionHandler(Connection conn,MyDataSource dataSource) {
            this.realConnection=conn;
            this.dataSource = dataSource;
        }
    
        Connection getWarpConn() {
            warpedConnection = (Connection) Proxy.newProxyInstance(this
                    .getClass().getClassLoader(), new Class[] { Connection.class },this);
            return warpedConnection;
        }
       //proxy:把代理对象自身传递进来 method:代表当前调用的方法 args:调用方法的参数
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable {
            if ("close".equals(method.getName())) {
                currentUserCount++;
                if (currentUserCount < maxUseCount)
                    dataSource.connectionsPool.addLast(warpedConnection);
                else {
                    realConnection.close();
                    dataSource.currentCount--;
                }
            }
            return method.invoke(realConnection, args);
        }
    }
    
    //2、动态代理改写之前的解决全站乱码拦截器   *经典
    public class CharacterEncodingFilter implements Filter {
    
        public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
                throws IOException, ServletException {
    
            final HttpServletRequest request = (HttpServletRequest) req;
            HttpServletResponse response = (HttpServletResponse) resp;
    
            request.setCharacterEncoding("UTF-8"); // 解决post乱码
    
            chain.doFilter((ServletRequest) Proxy.newProxyInstance(
                    CharacterEncodingFilter.class.getClassLoader(),
                    request.getClass().getInterfaces(), new InvocationHandler() {
    
                        public Object invoke(Object proxy, Method method, Object[] args) {
                            if (!method.getName().equals("getParameter")) {
                                return method.invoke(request, args);
                            }
                            if (!request.getMethod().equalsIgnoreCase("get")) {
                                return method.invoke(request, args);
                            }
    
                            String value = (String) method.invoke(request, args);
                            if (value == null) {
                                return null;
                            }
                            return new String(value.getBytes("iso8859-1"), "UTF-8");
                        }
    
                    }), response);
        }
    
        public void destroy() {
        }
    
        public void init(FilterConfig filterConfig) throws ServletException {
        }
    }
    
    //3、动态代理改写之前的压缩输出拦截器
    public class GzipFilter implements Filter {
    
        public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
                throws IOException, ServletException {
    
            final HttpServletRequest request = (HttpServletRequest) req;
            final HttpServletResponse response = (HttpServletResponse) resp;
    
            ResponseProxy proxy = new ResponseProxy(response);
            chain.doFilter(request, proxy.createProxy());
    
            byte[] out = proxy.getBuffer(); // 得到目标资源的输出
    
            System.out.println(new String(out, "UTF-8"));
    
        }
    
        class ResponseProxy {
            private ByteArrayOutputStream bout = new ByteArrayOutputStream();
            private PrintWriter pw = null;
    
            public byte[] getBuffer() {
                if (pw != null) {
                    pw.close();
                }
                return bout.toByteArray();
            }
    
            private HttpServletResponse response;
    
            public ResponseProxy(HttpServletResponse response) {
                this.response = response;
            }
    
            public HttpServletResponse createProxy() {
                return (HttpServletResponse) Proxy.newProxyInstance(GzipFilter.class.getClassLoader(),
                    response.getClass().getInterfaces(), new InvocationHandler() {
                        public Object invoke(Object proxy, Method method, Object[] args){
                            if (!method.getName().equals("getWriter")&&
                                    !method.getName().equals("getOutputStream")) {
                                method.invoke(response, args);
                            }
    
                            if (method.getName().equals("getWriter")) {//PrintWriter.write("中国");
                                pw = new PrintWriter(new OutputStreamWriter(bout, "UTF-8"));
                                return pw;
                            }
    
                            if (method.getName().equals("getOutputStream")) { 
                                return new ServletOutputStream() {
                                    @Override
                                    public void write(int b) throws IOException {
                                        bout.write(b);
                                    }
                                };
                            }
                            return null;
                        }
                    });
            }
        }
        public void destroy() {
        }
        public void init(FilterConfig filterConfig) throws ServletException {
        }
    }
    
    //4、动态代理+注解,实现权限管理
    //service方法中,权限可精确到具体的方法
    @Permission("添加分类")
    public void addCategory(Category c){
        cdao.add(c);
    }
    
    @Permission("查看分类")
    public Category findCategory(String id){
        return cdao.find(id);
    }
    
    public class ServiceFactory {
        
        private ServiceFactory(){}
        private static ServiceFactory instance = new ServiceFactory();
        public static ServiceFactory getInstance(){
            return instance;
        }
        
        public BusinessService createService(final User user){
            final BusinessService service = new BusinessServiceImpl();
            
            return (BusinessService) Proxy.newProxyInstance(ServiceFactory.class.getClassLoader(), 
                    service.getClass().getInterfaces(), new InvocationHandler(){
                
                public Object invoke(Object proxy, Method method, Object[] args)
                        throws Throwable {
                
                    //得到web层调用的方法
                    String methodName = method.getName();  //addCategory
                    
                    //反射出真实对象上相应的方法,检查真实对象方法上有没有权限注解
                    Method realMethod = service.getClass().getMethod(methodName, 
                            method.getParameterTypes());
                    Permission permission = realMethod.getAnnotation(Permission.class);
                    if(permission==null){
                        return method.invoke(service, args);
                    }
                    
                    //真实对象相应的方法上有权限注解,则得到访问该方法需要的权限
                    Privilege p = new Privilege(permission.value());//得到方法需要的权限
                    
                    //检查用户是否有权限  //AppContext ThreadLocal
                    //得到用户所有权限
                    if(user==null){
                        throw new SecurityException("您没有登陆");
                    }
                    
                    List<Privilege> list = service.getUserAllPrivilege(user);
                    if(list.contains(p)){
                        return method.invoke(service, args);
                    }
                    throw new SecurityException("你没有权限");
                }
            });
        }
    }
    
    public class CategoryServlet extends HttpServlet {
    
        public void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            
            String method = request.getParameter("method");
            if("add".equals(method)){
                add(request,response);
            }
            if("getAll".equals(method)){
                getAll(request,response);
            }
        }
    
        private void getAll(HttpServletRequest request, HttpServletResponse response) 
                throws ServletException, IOException {
            BusinessService service = ServiceFactory.getInstance().
                    createService((User)request.getSession().getAttribute("user"));
            try{
                List list = service.getAllCategory(); 
                request.setAttribute("categories", list);
                request.getRequestDispatcher("/manager/listcategory.jsp").forward(request, response);
            }catch (Exception e) {
                if(e.getCause() instanceof SecurityException){
                    request.setAttribute("message",  e.getCause().getMessage());
                }
            }
        }
    
        private void add(HttpServletRequest request, HttpServletResponse response) 
                throws ServletException, IOException {
            BusinessService service = ServiceFactory.getInstance().
                    createService((User)request.getSession().getAttribute("user"));
            try {
                Category c = WebUtils.request2Bean(request, Category.class);
                c.setId(UUID.randomUUID().toString());
                service.addCategory(c);
                request.setAttribute("message", "添加成功");
            } catch (Exception e) {
                if(e.getCause() instanceof SecurityException){
                    request.setAttribute("message", e.getCause().getMessage());
                }else{
                    e.printStackTrace();
                    request.setAttribute("message", "添加失败");
                }
            }
            request.getRequestDispatcher("/message.jsp").forward(request, response);
            
        }
    
        public void doPost(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            doGet(request, response);
        }
    
    }

    六、责任链模式

    参见:struts2基础中拦截器的实现原理.

    七、模板、策略模式

    模板方法:在定义功能时,功能的一部分是确定的,一部分是不确定的,那么就可以将不确定的部分暴露出去,让子类去完成.

    例:获取一段程序的执行时间
    abstract class GetRuntime{
      public final int getTime(){
        int start = System.currentTimeMillis();
        runCode();
        int end = System.currentTimeMillis();
        System.out.print(end - end)/1000;
      }
      abstract void runCode();
    }
    class Zi extends GetRuntime{
      void runCode(...)
      Zi z = new Zi();
      z.getTime();
    } 

    参见:javaEE(13)_jdbc框架

    八、享元模式

    java中常量池采用的就是享元设计模式,实际中做缓存时会采会用hashMap做一个享元,有的话直接拿,没有的话创建拿了再放进去.

    参见:javase(7)_Objcet类

    九、外观模式

    例如:service层的接口,封装所有的下层细节,对外暴漏简单的接口,这就是外观模式.

  • 相关阅读:
    Java中常见数据结构:List与Map
    JAVA 双重检查锁定和延迟初始化
    Spring 读取配置文件(二)
    Spring 读取配置文件(一)
    Java配置文件读取和路径设置
    动态设置spring配置PropertyPlaceholderConfigurer location的路径
    MySQL 数据库备份种类以及经常使用备份工具汇总
    打印二叉树两个叶子节点间的路径
    读书报告之《改动代码的艺术》 (I)
    虚幻引擎自带的创建插件的插件
  • 原文地址:https://www.cnblogs.com/wangweiNB/p/5218144.html
Copyright © 2020-2023  润新知