【导读】[开始]
设计模式虽多,最常用的也就十几种,一开始看不懂大神运用设计模式写的代码,感觉代码绕来绕去。当意识到自己的业务代码写的像流水账,自己都看不下去的时候,就知道该合理应用一些设计模式改善她们是必要的。简单总结工作中实践中接触到的15中设计模式使用要点。
【导读】[结束]
(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)原型模式
原型模式的目的是为了创建一个对象的拷贝。浅拷贝,深拷贝应用。
(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.
(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)模板方法模式
有一些业务逻辑部分一样、部分不同,那么就可以用模板方法模式把相同的部分确定下来,
把不同的部分进行方法抽象,等待子类实现。