设计模式
文章目录
一、设计模式分类
总体来说设计模式分为三大类:
-
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
-
结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
-
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
1、常见的设计模式
- 创建型模式:工厂方法模式 、 抽象工厂模式、单例模式、建造者模式 、原型模式
- 结构型模式:适配器模式 、装饰器模式 、代理模式 、桥接模式
- 行为型模式:命令模式、迭代器模式、策略模式、观察者模式
2、JDK中的体现
工厂方法模式
单列模式:Runtime;
抽象工厂模式:Calendar
适配器模式:IO 转换流(InputStreamReader、OutputStreamWriter )
装饰器模式:IO 流
策略模式:java.util.Comparator#compare() 、javax.servlet.http.HttpServlet 、javax.servlet.Filter#doFilter()
建造者模式:StringBuilder
二、六大原则
- 开闭原则:对扩展开发放,对修改关闭,抽象化是开闭原则的关键
- 单一职责原则:尽量使用合成/聚合的方式,而不是使用继承。一个类只负责一个功能领域中的相应职责
- 里式替换原则 :任何基类可以出现的地方,子类一定可以出现。
- 依赖倒转原则:依赖于抽象而不依赖于具体
- 接口隔离原则:使用多个隔离的接口,比使用单个接口要好 ,即客户端不应该依赖那些它不需要的接口。
- 迪米特法则:一个软件实体应当尽可能少地与其他实体发生相互作用。
设计详解
一、工厂模式(常见)
工厂模式可以分为三类:
1)简单工厂模式(Simple Factory):对象有个公共的接口 --> 不是23种类型中的一种
2)工厂方法模式(Factory Method) :有一个工厂类,通过判读创建不同的对象
JDK体现:java.lang.Integer#valueOf(String) (Boolean, Byte, Character,Short, Long, Float 和 Double与之类似)
3)抽象工厂模式(Abstract Factory):有个公共的抽象的工厂类,每个子类创建不同类型的对象
1、工厂方法模式
2、抽象工厂模式
二、单例模式(常见)
常见单例:懒汉式、饿汉式、登记式、枚举式(后三种天生线程安全)。
推荐使用饿汉式,明确实现lazy loading的效果,则使用登记方式;涉及反序列化使用枚举式,特殊条件可以考虑双检锁方式
jdk 体现:java.lang.Runtime#getRuntime()
1、单例模式特点
- 单例类只能有一个实例。
- 单例类必须自己创建自己的唯一实例。
- 单例类必须给所有其他对象提供这一实例。
优点:
- 在内存中只有一个对象,节省内存空间;
- 避免频繁的创建销毁对象,可以提高性能;
- 避免对共享资源的多重占用,简化访问;
- 为整个系统提供一个全局访问点
饿汉式和懒汉式区别
-
饿汉式:天生就是线程安全的,立即加载, 执行效率提高;容易产生垃圾对象
-
懒汉式:本身是非线程安全的,延迟加载;调用才初始化,避免内存浪费
扩展:
注意: 立即加载 和延迟加载
- 立即加载 : 在类加载初始化的时候就主动创建实例;
- 延迟加载 : 等到真正使用的时候才去创建实例,不用时不去
2、懒汉式单例
优缺点:延迟加载:调用才初始化,避免内存浪费,但不支持多线程
1> 不安全懒汉式
public class Singleton {
private static Singleton instance = null;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
懒汉式加synchronized 修饰,可以用于多线程
2> 安全型懒汉式
优缺点:可用于多线程,传说效率低,99%的情况不需要同步
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
3> 双检锁懒汉式
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
3、饿汉式单例
天生具体安全性,容易产生垃圾对象,属于立即加载
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
4、登记式/静态内部类
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
5、枚举
实现单例模式的最佳方法;天生具体安全性
class Resource{
}
public enum Singleton {
INSTANCE;
private Resource instance;
Singleton() {
instance = new Resource();
}
public Resource getInstance() {
return instance;
}
}
三、建造者模式(常见)
- 使用多个简单的对象一步一步构建成一个复杂的对象
- 如: 1、去肯德基,汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成出所谓的"套餐"
- JDK中的体现:StringBuilder
四、原型模式(常见)
定义:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
利用java克隆技术继承Cloneable重写clone、或者序列化和反序列化
浅拷贝:Object类的clone方法指对拷贝对象的基本类型,对数组,容器对象,引用对象等不会拷贝;
深拷贝:如果实现深拷贝,必须将原型模式中的数组,容器对象,引用对象等另行拷贝
五、适配器模式(常见)
定义:将两个原来不兼容的类兼容起来一起工作。
- **JDK中的体现:**IO流中的转换流;如:InputStreamReader、Arrays.asList()
- 例子:苹果手机电源转换插头
六、装饰器模式(常见)
定义:装饰器模式允许向一个现有的对象添加新的功能,同时又不改变其结构
-
**JDK中的体现:**IO流 中的不同类型的流,InputStream、OutputStream、
-
配一台台式电脑,配件有显示器,键盘、鼠标等,又有不同的品牌,则有:一个配件基类Components(定义了配件描述属性和输出描述的方法)、一个品牌基类Brand继承Components,配件基类有键盘和鼠标2个实现类,品牌基类有华为和联想2个实现类
//配件基类Components
public abstract class Compoments{
public String desc;
public abstract String getDesc();
}
//键盘
public class keyBoard extends Compoments{
public keyBoard(){
desc="键盘";
}
@Override
public String getDesc(){
return desc;
}
}
//鼠标
public class Mouse extends Compoments{
public Mouse(){
desc="键盘";
}
@Override
public String getDesc(){
return desc;
}
}
----------------------------------------
//基类Brand
public abstract class Brand extends Compoments{
@Override
public abstract String getDesc();
}
//huwei
public class Huawei extends Brand{
private Compoments compoments;
private String brand = "Huawei";
public Huawei(Compoments p){
this.compoments=p;
}
@Override
public String getDesc(){
return brand+compoments.desc;
}
}
//acer
public class Acer extends Brand{
private Compoments compoments;
private String brand = "Acer";
public Acer(Compoments p){
this.compoments=p;
}
@Override
public String getDesc(){
return brand+compoments.desc;
}
}
------------
//操作类
Compoments compoments = null;
//不要配件只要裸机
compoments = new keyBoard();
System.out.println(compoments.getDesc());
//----------------
compoments = new Acer(compoments);
System.out.println(compoments.getDesc());
优点:
- 装饰类和被装饰类可以独立发展
- 无耦合
- 装饰模式是继承的一个替代模式
- 装饰模式可以动态扩展一个实现类的功能。
缺点:多层装饰比较复杂
七、代理模式(常见)
定义:将原类进行封装,为其他对象提供一个代理,以控制对当前对象的访问。
- 静态代理:被代理对象与代理对象一起实现相同的接口或者是继承相同父类.
- jdk动态代理:java.lang.reflect.Proxy.newProxyInstance() 和InvocationHandler对象
- cglib动态代理:实现MethodInterceptor 接口
/**
* 静态代理
*/
public interface HelloService {
String hello(String name);
}
public class HelloServiceImpl implements HelloService{
@Override
public String hello(String name) {
return "Hello " + name;
}
}
//代理类
public class HelloServiceProxy implements HelloService {
private HelloService helloService;
public HelloServiceProxy(HelloService helloService) {
this.helloService = helloService;
}
@Override
public String hello(String name) {
System.out.println("预处理...");
String result = helloService.hello(name);
System.out.println(result);
System.out.println("后处理...");
return result;
}
}
//测试
HelloService helloService = new HelloServiceImpl();
HelloServiceProxy helloServiceProxy = new HelloServiceProxy(helloService);
helloServiceProxy.hello("Panda");
//-------------------------------------------------------------------
/**
* 创建JDK动态代理对象
* 动态代理不需要实现接口,但是需要指定接口类型
*/
public class ProxyFactory{
//维护一个目标对象
private Object target;
public ProxyFactory(Object target){
this.target=target;
}
//给目标对象生成代理对象
public Object getProxyInstance(){
return Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开始事务2");
//执行目标对象方法
Object returnValue = method.invoke(target, args);
System.out.println("提交事务2");
return returnValue;
}
}
);
}
}
/**
* Cglib子类代理工厂
* 对UserDao在内存中动态构建一个子类对象
*/
public class ProxyFactory implements MethodInterceptor{
//维护目标对象
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
//给目标对象创建一个代理对象
public Object getProxyInstance(){
//1.工具类
Enhancer en = new Enhancer();
//2.设置父类
en.setSuperclass(target.getClass());
//3.设置回调函数
en.setCallback(this);
//4.创建子类(代理对象)
return en.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("开始事务...");
//执行目标对象的方法
Object returnValue = method.invoke(target, args);
System.out.println("提交事务...");
return returnValue;
}
}
八、外观模式
- 定义:隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口
- 构造方法中创建一个接口的所有子对象,并提供方法来访问子对象中的方法
- jdk 中的体现:Class
九、桥接模式
十、组合模式
十一、享元模式
-
享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能。
-
String常量池、数据库连接池、缓冲池等等都是享元模式的应用,所以说享元模式是池技术的重要实现方式
-
java对于Integer与int的自动装箱与拆箱的设计,是一种模式:叫享元模式(flyweight)
-
**如何解决:**用唯一标识码判断,如果在内存中有,则返回这个唯一标识码所标识的对象。
**关键代码:**工厂类中用 HashMap 存储这些对象。
import java.util.HashMap;
public class ShapeFactory {
private static final HashMap<String, Shape> circleMap = new HashMap<>();
public static Shape getCircle(String color) {
Circle circle = (Circle)circleMap.get(color);
if(circle == null) {
circle = new Circle(color);
circleMap.put(color, circle);
System.out.println("Creating circle of color : " + color);
}
return circle;
}
}
十二、策略模式(常见)
-
定义:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。
-
**JDK中的体现:**ThreadPoolExecutor(提供了四种策略)
-
策略模式和工厂方法模式,有点像,工厂方法模式是关注对象的创建,策略模式是关注行为的封装
十三、模板方法模式
十四、观察者模式(常见)
定义:定义对象间的一种一对多的依赖关系;当一个对象被修改时,则会自动通知它的依赖对象。观察者模式属于行为型模式。
关键点:在抽象类里有一个 ArrayList 存放观察者们。
例子:拍卖师(Auction)每次宣布一件物品(goods)开始竞拍,竞价者(Bidder)听到指令开始竞价
public class Auction{
private String goods;
private ListArray<Bidder> bidders= new ArrayList<Bidder>();
public void setGoods(String goods){
this.goods=goods;
notifyAllBidder();
}
public String getGoods(){
return goods;
}
public void attach(Bidder b){
bidders.add(b);
}
public void notifyAllBidder(){
for (Bidder b : bidders) {
b.update();
}
}
}
public abstract class Bidder {
protected Auction auction;
public abstract void update();
}
//路人A Aperson
public class ApersonBidder extends Bidder{
public ApersonBidder(Auction a){
this.auction = a;
this.auction.attach(this);
}
@Override
public void update() {
System.out.println(this.auction.getGoods() + "--A出价10万 ");
}
}
//路人B Bperson
public class BpersonBidder extends Bidder{
public BpersonBidder(Auction a){
this.auction = a;
this.auction.attach(this);
}
@Override
public void update() {
System.out.println( this.auction.getGoods() + "--B出价30万 ");
}
}
//操作类
Auction auction = new Auction();
new ApersonBidder(auction);
new BpersonBidder(auction);
subject.setState("瓷器");
subject.setState("字画");
十五、迭代子模式
十六、责任链模式
十七、命令模式
十八、备忘录模式
十九、状态模式
二十、访问者模式
二十一、中介者模式
定义: 定义一个中介对象来封装系列对象之间的交互
例子:中介公司