这是我参与更文挑战的第5天
设计模式
这里说一些自己面试碰到的模式,以及读源码的时候看到的一些设计模式。
1.单例模式:
1.目的:
解决一个全局使用的类频繁地创建与销毁,在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存),避免对资源的多重占用(比如写文件操作)。
2.实现:
将构造类设为私有,具体的实现方法有:饱汉式、恶汉式、内部静态类、enum枚举
具体代码实现参见:
- 简单实现
public class SingletonPattern {
//将类的构造方法设置为私有
private SingletonPattern(){};
//使用static final 修饰最终new的对象,保证唯一
//有个缺点就是,当我们不使用INSTANCE的时候,希望INSTANCE不要申请内存空间
//用的时候再申请
//当前这种方法称之为饱汉式
private static final SingletonPattern INSTANCE = new SingletonPattern();
public static SingletonPattern getSingletonPattern(){
return INSTANCE;
}
复制代码
- 上述代码存在的问题是:当我们不使用SingletonPattern的时候,但是程序却给我们创建了SingletonPattern实例,浪费内存资源,所以采用懒汉式加载,只有在使用的时候才创建:(至于线程安全,程序注释有说明)
public class SingletonPattern2 {
//将类的构造方法设置为私有
private SingletonPattern2(){};
//使用static final 修饰最终new的对象,保证唯一
//开始的时候对象置为空,在需要的时候,再去申请内存空间
//这种模式称之为懒汉式,但是这种模式再多线程场景中,无法保证对象是单实例
//当A、B两个线程同时进入if(INSTANCE == null)中,就会同时创建两个SingletonPattern2
private static volatile SingletonPattern2 INSTANCE = null;
//1.在方法上锁,影响效率,将锁的范围缩小
public static /*synchronized*/ SingletonPattern2 getSingletonPattern() throws InterruptedException {
if (INSTANCE == null) {
//在判断里面上锁,仍然存在A、B线程出现的问题
synchronized (SingletonPattern.class) {
if(INSTANCE == null) {
Thread.sleep(1);
INSTANCE = new SingletonPattern2();
}
}
}
return INSTANCE;
}
}
复制代码
- 关于线程安全的另外两种创建方式:
- 内部静态类
public class SingletonPattern3 { //将类的构造方法设置为私有 private SingletonPattern3(){}; //设置静态内部类 public static class SingletonPattern3Holder{ private final static SingletonPattern3 INSTANCE = new SingletonPattern3(); } public static SingletonPattern3 getInstance(){ return SingletonPattern3Holder.INSTANCE; } } 复制代码
- 枚举
public enum SingletonPattern4 { INSTANCE; //验证不是单实例的问题,即线程不安全 public static void main(String[] args) { Map<Integer, Integer>map = new HashMap<>(); for(int i=0; i<100; i++){ new Thread(()->{ System.out.println(SingletonPattern4.INSTANCE.hashCode()); }).start(); } } } 复制代码
问题:枚举为什可以保证线程安全:
从两个方面来说:
-
Java规范中规定,每一个枚举类型极其定义的枚举变量在JVM中都是唯一的
-
枚举的变量经过反编译是由static修饰的,而JVM会保证一个类的*()* 方法在多线程环境中被正确的加锁、同步
3.缺点:
没有接口,无法继承,违反了单一原则,即一个类应该只关心内部逻辑,而应该关心怎么实例化
2.工厂模式:
1.目的:
定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。是基于产品的维度考虑。
- 调用者想创建一个对象,只要知道其名称就可以了。
- 扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。
- 屏蔽产品的具体实现,调用者只关心产品的接口。
2.实现
1.定义一个功能类接口,然后具体产品类实现接口,覆写方法。最终在一个xxFactory中创建实现对各产品类的创建 new Xxx
public interface MoveAble {
public void go();
}
复制代码
汽车实现该接口方法
Car.java
public class Car implements MoveAble {
@Override
public void go(){
System.out.println("Car go wwwwwwwwwww.....");
}
}
复制代码
飞机实现该接口方法
Plane.java
public class Car implements MoveAble {
@Override
public void go(){
System.out.println("Car go wwwwwwwwwww.....");
}
}
复制代码
实现Car的生产工厂CarFactory.java
//工厂方法基于产品维度扩展
public class CarFactory {
public Car createCar(){
System.out.println("a car is created!");
return new Car();
}
}
复制代码
于是在生产Car的时候,只需要调用CarFactory中的createCar方法创建即可。从这个例子来看,工厂模式是处于产品的维度。
3.抽象工厂模式(Abstract Factory Pattern)
1.目的:
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。同样解决接口选择的问题。抽象工厂处于产品族的维度
2.实现:
在一个工厂里聚合多个同类产品。
3.缺点:
产品族扩展非常困难,要增加一个系列的某一产品,既要在抽象的 Creator 里加代码,又要在具体的里面加代码。
4.代码实现
abstractfactory
//基于产品一族进行扩展
public abstract class AbstractFactory {
public abstract Food createFood(); //食物
public abstract Vehicle createVehicle(); //交通工具
public abstract Weapon createWeapon(); //武器
}
复制代码
然后将这些分为两大工厂,魔法世界的,现代社会都市的,全部继承抽象工厂
MagricFactory.java
public class MagricFactory extends AbstractFactory{
@Override
public Food createFood() {
return new MushRoom();
}
@Override
public Vehicle createVehicle() {
return new Broom();
}
@Override
public Weapon createWeapon() {
return new MagicStick();
}
}
复制代码
ModernFactory.java
public class ModernFactory extends AbstractFactory{
@Override
public Food createFood() {
return new Bread();
}
@Override
public Vehicle createVehicle() {
return new Car();
}
@Override
public Weapon createWeapon() {
return new AK47();
}
}
复制代码
然后分別实现两个世界中对应的实体类,这里就不一一写出了,只写两个:魔法世界的食物,和现代社会都市的食物:
MushRoom.java
public class MushRoom extends Food{
@Override
public void printName() {
System.out.println("du mo gu");
}
}
复制代码
Bread.java
public class Bread extends Food{
public void printName(){
System.out.println("好吃");
}
}
复制代码
4.责任链模式
1.目的:
责任链模式避免了请求的发送者和接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连成一条链,并且沿着这条链传递请求,直到有对象处理它为止。
- 降低耦合度。它将请求的发送者和接收者解耦
- 简化了对象,使得对象不需要知道链的结构
- 增强给对象指派职责的灵活性,允许动态地新增或者删除责任链
- 增加新的请求处理类方便
2.实现:
Filter里面聚合它自己,在 FilterRequest 里判断是否合适,如果没达到条件则向下传递,向谁传递之前, 先将Filter set 进去。
package com.anzhi.filterRequestResponse;
import java.util.ArrayList;
import java.util.List;
/**
*用Filter模拟处理Request、Response
*思路细节技巧:
* 1. Filter的doFilter方法改为doFilter(Request,Resopnse,FilterChain),
* 有FilterChain引用,为利用FilterChain调用下一个Filter做准备
*
* 2.FilterChain继承Filter,这样,FilterChain既是FilterChain又是Filter,
* 那么FilterChain就可以调用Filter的方法doFilter(Request,Resopnse,FilterChain)
*
* 3. FilterChain的doFilter(Request,Resopnse,FilterChain)中,有index标记了执行到第几个Filter,
* 当所有Filter执行完后request处理后,就会return,以倒序继续执行response处理
*
* */
public class FilterRequestResponse {
public static void main(String[] args) {
String msg = "<html>敏感字眼</html>";
Request request = new Request();
Response response = new Response();
request.setStr(msg);
response.setStr(msg);
FilterChain filterChain = new FilterChain();
filterChain.addFIlter(new HTMLFilter()).addFIlter(new SensitiveFilter());
filterChain.doFilter(request,response, filterChain);
System.out.println(request.getStr());
System.out.println(response.getStr());
}
}
class Request{
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
private String str;
}
class Response{
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
private String str;
}
interface Filter{
public void doFilter(Request request, Response response, FilterChain filterChain);
}
class FilterChain implements Filter{
//做判断
private List<Filter> filters = new ArrayList<>();
int index = 0; //标记执行到第几个filter
//把函数的返回值设为FilterChain返回this,就能方便链式编写代码
public FilterChain addFIlter(Filter filter){
filters.add(filter);
return this;
}
@Override
public void doFilter(Request request, Response response, FilterChain filterChain) {
//这里是一个回链,不能中间断开
if(index == filters.size()){
return;
}
Filter f = filters.get(index);
index++; //记录相应filter在数组中的下标调用
f.doFilter(request,response,filterChain);
}
}
class HTMLFilter implements Filter{
@Override
public void doFilter(Request request, Response response,
FilterChain filterChain) {
request.setStr(request.getStr().replace('<', '[').replace(">", "]")+"---HTMLFilter()");
filterChain.doFilter(request, response, filterChain);
response.setStr(response.getStr()+"---HTMLFilter()");
}
}
class SensitiveFilter implements Filter{
@Override
public void doFilter(Request request, Response response,
FilterChain filterChain) {
request.setStr(request.getStr().replace("敏感", "幸福")+"---SensitiveFilter()");
filterChain.doFilter(request, response, filterChain);
response.setStr(response.getStr()+"---SensitiveFilter()");
}
}
复制代码
3.缺点:
- 不能保证请求一定被接收;
- 系统性能将受到一定影响,调试时不方便,可能会造成循环调用
5.模版方法:
1.目的:
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。一些方法通用,却在每一个子类都重新写了这一方法。
- 封装不变部分,扩展可变部分。
- 提取公共代码,便于维护。
- 行为由父类控制,子类实现。
2.实现:
在抽象类实现方法,其他步骤在子类实现。