架构思想|设计模式实践总结篇

【导读】[开始]
设计模式虽多,最常用的也就十几种,一开始看不懂大神运用设计模式写的代码,感觉代码绕来绕去。当意识到自己的业务代码写的像流水账,自己都看不下去的时候,就知道该合理应用一些设计模式改善她们是必要的。简单总结工作中实践中接触到的15中设计模式使用要点。
【导读】[结束]

1 创建模式

(1)单例模式

1 饿汉式单例-线程安全
2 懒汉单例模式-线程不安全
3 加锁,双重锁校验优化后的懒汉单例模式
4 **使用静态内部类实现单例**-线程安全【工作中常用】
 对象序列化,然后反序列化,会破坏单例
5 枚举构建单例-线程安全,避免序列化问题(貌似实际工作总用的很少)

【附静态内部类单例】
public class ThreadPoolFactory {
    /**
     * 获取定时调度的线程池
     * @return
     */
    public static ScheduledExecutorService getScheduledPool() {
        return ScheduledThreadPoolHolder.ScheduledPool;
    }
    /**
     * 获取自定义的线程池
     * @return
     */
    public static ThreadPoolExecutor getPoolExecutor() {
        return ThreadPoolExecutorHolder.poolExecutor;
    }
    /**
     * 设置线程池
     * @author bilahepan
     * @version $Id: ThreadPoolFactory.java, v 0.1 2018年1月5日 上午10:56:19 bilahepan Exp $
     */
    public static class CachedThreadPoolHolder {
        /** 线程池 */
        private static final ExecutorService POOL = Executors.newCachedThreadPool();
    }
    /**
     * 定时调度线程池
     * @author bilahepan
     * @version $Id: ThreadPoolFactory.java, v 0.1 2018年1月5日 上午10:58:21 bilahepan Exp $
     */
    public static class ScheduledThreadPoolHolder {
        /** 线程池 */
        private static final ScheduledExecutorService ScheduledPool = new ScheduledThreadPoolExecutor(5);
    }
    /**
     * 创建自定义的线程池
     * @author bilahepan
     * @version $Id: ThreadPoolFactory.java, v 0.1 2018年1月5日 下午2:12:27 bilahepan Exp $
     */
    public static class ThreadPoolExecutorHolder {
        /** 线程池 */
        private static final ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(40, 80, 30, TimeUnit.MINUTES, new LinkedBlockingQueue<Runnable>(),
            new ThreadPoolExecutor.DiscardPolicy());
    }
}

(2)工厂方法模式

	有简单工厂,静态工厂,抽象工厂等。抽象工厂模式应用较多。
抽象工厂模式就是创建多个工厂类,一旦需要增加新的功能,直接增加新的工厂类就可以了。

(3)构造者模式

	工作中貌似还没用到很复杂的构造者模式。
	ElasticSearch的java-api中有很多构造者模式应用。
	如下的情景可以改造使用下:
	如果一个类有很多的构造函数参数,这些参数有各种不同组合的重载,有的必传,有的非必传,那么就会需要N多重载构造函数。
如果搞成一个无参构造函数,属性值以set方法来提供,那么就会存在不合法的对象状态的可能性。可用构造者模式优化。

【示例】
/**
 * 
 * @author bilahepan
 * @version $Id: User.java, v 0.1 2018年5月6日 下午8:13:18 bilahepan Exp $
 */
public class User {
    private String firstName; // 必传参数
    private String lastName;  // 必传参数
    private int    age;       // 可选参数
    private String phone;     // 可选参数
    private String address;   // 可选参数
    private User(Builder builder) {
        this.firstName = builder.firstName;
        this.lastName = builder.lastName;
        this.age = builder.age;
        this.phone = builder.phone;
        this.address = builder.address;
    }
    /** 无需setter方法 */
    public String getFirstName() {
        return firstName;
    }
    public String getLastName() {
        return lastName;
    }
    public int getAge() {
        return age;
    }
    public String getPhone() {
        return phone;
    }
    public String getAddress() {
        return address;
    }
    /** 
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return "User [firstName=" + firstName + ", lastName=" + lastName + ", age=" + age + ", phone=" + phone + ", address=" + address + "]";
    }
    
    /**
     * 静态内部类--隐藏
     * @author bilahepan
     * @version $Id: User.java, v 0.1 2018年5月6日 下午8:15:48 bilahepan Exp $
     */
    public static class Builder {
        private String firstName; // 必传参数
        private String lastName;  // 必传参数
        private int    age;       // 可选参数
        private String phone;     // 可选参数
        private String address;   // 可选参数
        public Builder(String firstName, String lastName) {
            //this--引用隐式参数
            this.firstName = firstName;
            this.lastName = lastName;
        }
        public Builder age(int age) {
            this.age = age;
            //this--代表Builder的一个对象
            return this;
        }
        public Builder phone(String phone) {
            this.phone = phone;
            return this;
        }
        public Builder address(String address) {
            this.address = address;
            return this;
        }
        public User build() {
            return new User(this);
        }
    }

//----------------------------------------------
 public static void main(String[] args) {
        User user = new User.Builder("bila", "juan").age(18).build();
        System.out.println(user);
    }

(4)原型模式

	原型模式的目的是为了创建一个对象的拷贝。浅拷贝,深拷贝应用。

2 结构模式

(1)外观模式

	屏蔽内部实现差异,对外提供统一的操作接口,简化操作。
	工作中体现非常之多,每一个服务对外暴露一系列Facade门面接口;服务内部分Biz层,Integration层等。

(2)适配器模式

	需要将“一些现存的对象”放在新的环境中应用,但是新环境要求的接口是这些现存对象所不满足的。
那么就可以创建一个适配器(转换器)让旧对象适用于新环境。新老接口做兼容情景等很常用。

(3)装饰者模式

	如果想给一些实现类加多个额外的功能,且不能修改类的原有实现,那么就可以实现装饰者模式。
装饰者类实现的是和被装饰的类相同的接口,可以代替被装饰的类出现,主要功能还是转发给具体实现类实现,
只有特殊功能才由自己实现。如Collections的synchronizedList等把线程不安全的集合转换为线程安全的
集合的方法。IO类中的装饰者模式。

(4)代理模式

//jdk-aop切面代理    
//cglib动态代理  
//Hessian的原理也是动态生成类,动态生成的类实现了接口,接口的实现是把请求
转化为Hessian格式发文,发给服务器,并且解析返回值。

【cglib-动态代理】
public class MyCglibProxy implements MethodInterceptor {
    private Object target;
    /**
     * 创建代理对象
     * @param target
     * @return
     */
    public Object getInstance(Object target) {
        this.target = target;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.target.getClass());
        // 回调方法
        enhancer.setCallback(this);
        // 创建代理对象
        return enhancer.create();
    }

    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println(method.getName() + "方法开始之前");
        //调用目标方法
        Object result = methodProxy.invokeSuper(proxy, args);
        System.out.println(method.getName() + "方法开始之后");
        return result;
    }
}
//--------------------
public class MyMethod {
    public void sing() {
        System.out.println("sing~~~");
    }
    public void song(String name) {
        System.out.println("song~~~" + name);
    }
}
//--------------------
/**
 * cglib动态代理使用
 * 原理是生成MyMethod类的子类,然后对所有的方法都override,因此final方法不能被拦截。
 * @param args
 */
  public static void main(String[] args) {
      MyMethod proxyMethod = (MyMethod) new MyCglibProxy().getInstance(new MyMethod());
      proxyMethod.sing();
      proxyMethod.song("<以后的以后>");
  }


【jdk动态代理】
public class MyDynamicProxy implements InvocationHandler {
    /** 需要代理的目标对象 */
    private Object target;
    /**
     * 写法固定,aop专用:绑定委托对象并返回一个代理类
     * @param delegate
     * @return
     */
    public Object bind(Object target) {
        this.target = target;
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    /**
     * @param Object
     *            target:指被代理的对象。
     * @param Method
     *            method:要调用的方法
     * @param Object
     *            [] args:方法调用时所需要的参数
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(method.getName() + "执行之前");
        //执行目标方法
        Object result = method.invoke(target, args);
        System.out.println(method.getName() + "执行之后");
        return result;
    }
}
//--------------------
public class MyMethod     {
    /**
     */
    public void go() {
        System.out.println("gogogo!");
    }
    /**
     * @param str
     */
    public void back(String str) {
        System.out.println("back:" + str);
    }
}
//--------------------
public class DemoTest {
    public static void main(String[] args) {
        MyMethod myMethod = new MyMethod();
        MyMethod proxyMethod = (MyMethod) new MyDynamicProxy().bind(myMethod);
        proxyMethod.go();
        proxyMethod.back("haha");
    }
}

(5)享元模式

	享元模式的目的就是为了降低内存的占用,对于同样用途的对象,尽可能复用同一个对象。
简单说,**资源池**的应用场景就是享元模式。
应用1:字符串常量池
应用2:Integer常量池[-128,127]
应用3:数据库连接池

(6)组合模式

	组合模式为处理树形结构提供了完美的解决方案,描述了如何将容器和叶子进行递归组合,使得用户在使用时可以一致性地
对待容器和叶子。通过递归的方式处理所有子节点和后代节点。容器节点和叶子结点有公共父类或者公共接口,这样就可以一致
的对待容器节点和叶子结点。
	JDK中用File代表文件和文件夹,isFile()表示是否是文件,isDirectory()表示是否是文件夹,listFiles()获取下
一级的文件、文件夹。虽然不是“用一个类作为容器节点和叶子节点的公共父类”,但是还是“组合模式”的灵魂。
	org.w3c.dom.Node遍历xml文件,所有Xml节点都抽象为Node类型,Node中getChildNodes()为获取子节点,对于叶子
节点来说子节点个数为0.

3 行为模式##

(1)迭代器模式

	有没有下一条数据,如果有的话则取下一条数据。实现迭代器接口的集合类。

(2)命令模式

	命令模式可以把命令的“执行”和命令的“安排”分离,也就是把“怎么干”和“什么时候干”分离。
示例:

public class RetryRunnable implements Runnable {
    /** 
     * @see java.lang.Runnable#run()
     */
    @Override
    public void run() {
    }
}

//---------------------
public class Retry {
    /**
     * 最多执行task任务maxTimes多次
     * @param maxTimes
     * @param task
     * @return 是否执行成功
     */
    public static boolean execute(int maxTimes, RetryRunnable task) {
        for (int i = 0; i < maxTimes; i++) {
            try {
                //什么时候做
                task.run();
                //执行到这里说错执行没问题
                return true;
            } catch (Exception ex) {
                System.out.println("执行出现异常" + ex);
            }
        }
        //执行到这里说明执行出错
        return false;
    }
}

//---------------------
public class DemoTest {
    @org.junit.Test
    public void test() {
        boolean b = Retry.execute(3, new RetryRunnable() {
            //怎么做
            @Override
            public void run() {
                try {
                    URL url = new URL("http://www.ly.com");
                    String html = IOUtils.toString(url, "UTF-8");
                    System.out.println(html);
                } catch (Exception e) {
                    System.out.println(e.getMessage());
                }
            }
        });
        System.out.println("执行结果" + b);
    }
}

(3)策略模式

	干一件事情的时候有多个类似的算法,在不同情况下用不同的算法。
	对于非基本类型Arrays类提供了sort排序算法,排序算法是固定的,但是算法确定不了的是“如何比较两个对象的大小”,
因此把这个比较的算法通过策略接口Comparator开放出来。 Comparator比较器实现,进行排序,比较。

【示例】
public interface DigestStrategy {
    String digest(String data);
}
//-----------
public class MD5StrategyImpl implements DigestStrategy {
    @Override
    public String digest(String data) {
        return DigestUtils.md5Hex(data);
    }
}
//-----------
public class SHA1StrategyImpl implements DigestStrategy {
    @Override
    public String digest(String data) {
        return DigestUtils.sha1Hex(data);
    }
}
//-----------
public class CalculateSecretkey {
    /**  */
    private DigestStrategy digestStrgety;
    
    public CalculateSecretkey(DigestStrategy digestStrgety) {
        super();
        this.digestStrgety = digestStrgety;
    }

    /**
     * 加密
     * @param value
     * @return
     */
    public String calcSecStr(String value) {
        return digestStrgety.digest(value);
    }
}
//-----------
public class DemoTest {
    /**
     * @param args
     */
    public static void main(String[] args) {
        CalculateSecretkey calcKey = new CalculateSecretkey(new MD5StrategyImpl());
        String hash = calcKey.calcSecStr("1234567");
        System.out.println(hash);
    }
}

(4)责任链模式

	“一个请求可以被多个对象处理,但是每个请求在运行时只能有一个接受者,如果显示指定,将必不可少地带来请求发送者
与接受者的紧耦合。如何使请求的发送者不需要指定具体的接受者,让请求的接受者自己在运行时决定来处理请求,从而使两者
解耦。”如获取数据库连接驱动,传入驱动名,数据库连接地址,就可以获取数据库连接对象。

【示例】
/**
 * 文本处理接口
 * @author bilahepan
 * @version $Id: FileReader.java, v 0.1 2018年5月4日 上午12:29:40 bilahepan Exp $
 */
public interface FileReader {
    
    /**
     * 给定的文件file是否能被这个类处理
     * @param file
     * @return
     */
    boolean accept(File file);
    
    /**
     * 读取文件file,读取它的文本内容
     * @param file
     * @return
     * @throws IOException
     */
    String readAsString(File file) throws IOException;
}
//----------------
public class DocFileReaderImpl implements FileReader {
    /** 
     * @see com.ly.bilahepan.designpattern.behaviorPattern.ChainOfResponsibilityPattern.FileReader#accept(java.io.File)
     */
    @Override
    public boolean accept(File file) {
        String ext = FilenameUtils.getExtension(file.getName());
        return ext.equalsIgnoreCase("doc");
    }
    /** 
     * @see com.ly.bilahepan.designpattern.behaviorPattern.ChainOfResponsibilityPattern.FileReader#readAsString(java.io.File)
     */
    @Override
    public String readAsString(File file) {
        try {
            FileInputStream fis = new FileInputStream(file);
            HWPFDocument doc = new HWPFDocument(fis);
            Range rang = doc.getRange();
            String text = rang.text();
            return text;
        } catch (Exception e) {
            return "";
        }
    }
}
//----------------
public class DocxFileReaderImpl implements FileReader {
    /** 
     * @see com.ly.bilahepan.designpattern.behaviorPattern.ChainOfResponsibilityPattern.FileReader#accept(java.io.File)
     */
    @Override
    public boolean accept(File file) {
        String ext = FilenameUtils.getExtension(file.getName());
        return ext.equalsIgnoreCase("docx");
    }
    /** 
     * @see com.ly.bilahepan.designpattern.behaviorPattern.ChainOfResponsibilityPattern.FileReader#readAsString(java.io.File)
     */
    @SuppressWarnings("resource")
    @Override
    public String readAsString(File file) {
        try {
            FileInputStream fis = new FileInputStream(file);
            XWPFDocument xdoc = new XWPFDocument(fis);
            XWPFWordExtractor extractor = new XWPFWordExtractor(xdoc);
            String text = extractor.getText();
            return text;
        } catch (Exception e) {
            return "";
        }
    }
}
//----------------
public class HtmlFileReaderImpl implements FileReader {
    @Override
    public boolean accept(File file) {
        String ext = FilenameUtils.getExtension(file.getName());
        return ext.equalsIgnoreCase("html") || ext.equalsIgnoreCase("htm");
    }
    @Override
    public String readAsString(File file) throws IOException {
        Document doc = Jsoup.parse(file, null);
        return doc.text();
    }
}

//----------------
public class TXTFileReaderImpl implements FileReader {
    /** 
     * @see com.ly.bilahepan.designpattern.behaviorPattern.ChainOfResponsibilityPattern.FileReader#accept(java.io.File)
     */
    @Override
    public boolean accept(File file) {
        String ext = FilenameUtils.getExtension(file.getName());
        return ext.equalsIgnoreCase("txt");
    }
    /** 
     * @see com.ly.bilahepan.designpattern.behaviorPattern.ChainOfResponsibilityPattern.FileReader#readAsString(java.io.File)
     */
    @Override
    public String readAsString(File file) throws IOException {
        return FileUtils.readFileToString(file, Charset.forName("UTF-8"));
    }
}
//----------------
/**
  ----------
     父类静态变量
    父类静态代码块
    子类静态变量
    子类静态代码块
    父类非静态变量
    父类非静态代码块
    父类构造器
    子类非静态变量
    子类非静态代码块
    子类构造器
  ----------
  静态对象(变量)由于非静态对象(变量)初始化;
  其中静态对象(变量)只初始化一次,而非静态对象(变量)可能会初始化很多次;父类优先于子类进行初始化。
 https://www.cnblogs.com/lubocsu/p/5099558.html
  ----------
 * 执行器
 * @author bilahepan
 * @version $Id: TextExecutor.java, v 0.1 2018年5月4日 上午12:29:20 bilahepan Exp $
 */
public class TextExecutor {
    //所有实现了TextProcessor接口的类的对象
    private static LinkedList<FileReader> readerList = new LinkedList<>();
    //初始化时,注册处理
    static {
        DocFileReaderImpl docFileReader = new DocFileReaderImpl();
        DocxFileReaderImpl docxFileReader = new DocxFileReaderImpl();
        HtmlFileReaderImpl htmlFileReader = new HtmlFileReaderImpl();
        TXTFileReaderImpl txtFileReader = new TXTFileReaderImpl();
        readerList.add(docFileReader);
        readerList.add(docxFileReader);
        readerList.add(htmlFileReader);
        readerList.add(txtFileReader);
    }

    /**
     * 文本处理器
     * @param file
     * @return
     * @throws IOException
     */
    public static String readTextAsStr(File file) throws IOException {
        //遍历每个processor
        for (FileReader processor : readerList) {
            //这个文件是否能被当前遍历的processor处理
            if (processor.accept(file)) {
                //调用processor读取内容
                return processor.readAsString(file);
            }
        }
        throw new RuntimeException("没有找到合适的阅读器");
    }
}
//----------------
public class DemoTest {
    public static void main(String[] args) throws IOException {
        String str = TextExecutor.readTextAsStr(new File("F:file.txt"));
        System.out.println(str);
    }
}

(5)模板方法模式

	有一些业务逻辑部分一样、部分不同,那么就可以用模板方法模式把相同的部分确定下来,
把不同的部分进行方法抽象,等待子类实现。

你可能感兴趣的:(【编程之法内功】)