Java常用的设计模式
文章目录
- 1. 设计模式的特点与简介
- 1.1 什么是设计模式
- 1.2 Java中的设计模式
- 2. 工厂模式
- 2.1 简单工厂模式
- 2.2 小结
- 3. 代理模式
- 3.1 动态代理
- 3.2 JDK动态代理
- 3.3 小结
- 4. 单例模式
- 4.1 懒汉式单例与饿汉式单例
- 4.2 小结
- 5. 策略模式
- 5.1 策略模式
- 5.2小结
1. 设计模式的特点与简介
1.1 什么是设计模式
设计模式(Design pattern):代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。
简而言之:就是书写代码的一些规律,例如经常开车的人都知道,开车要用一档起步
1.2 Java中的设计模式
java的设计模式大体上分为三大类:一共23种
- 创建型模式(
5
种):工厂方法模式
,抽象工厂模式
,单例模式
,建造者模式
,原型模式
。 - 结构型模式(
7
种):适配器模式
,装饰器模式
,代理模式
,外观模式
,桥接模式
,组合模式
,享元模式
。 - 行为型模式(
11
种):策略模式
、模板方法模式
、观察者模式
、迭代子模式
、责任链模式
、命令模式
、备忘录模式
、状态模式
、访问者模式
、中介者模式
、解释器模式
。
设计模式遵循的原则有6个:
- 开闭原则(
Open Close Principle
):对扩展开放,对修改关闭。 - 里氏代换原则(
Liskov Substitution Principle
):只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。 - 依赖倒转原则(
Dependence Inversion Principle
):这个是开闭原则的基础,对接口编程,依赖于抽象而不依赖于具体。 - 接口隔离原则(
Interface Segregation Principle
):使用多个隔离的接口来降低耦合度。 - 迪米特法则(最少知道原则)(
Demeter Principle
):一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。 - 合成复用原则(
Composite Reuse Principle
)原则是尽量使用合成/聚合的方式,而不是使用继承。继承实际上破坏了类的封装性,超类的方法可能会被子类修改。
2. 工厂模式
2.1 简单工厂模式
定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使一个类的实例化延迟到其子类。
工厂方法即Factory Method,是一种对象创建型模式。
工厂方法的目的是使得创建对象和使用对象是分离的,并且客户端总是引用抽象工厂和抽象产品:
┌─────────────┐ ┌─────────────┐
│ Product │ │ Factory │
└─────────────┘ └─────────────┘▲ ▲│ │
┌─────────────┐ ┌─────────────┐
│ ProductImpl │<─ ─ ─│ FactoryImpl │
└─────────────┘ └─────────────┘
举例代码:
车的接口
package com.rj.bd.lx.lx01;public interface ICar {//制造不同零件方法public void make();
}
车接口的实现类
package com.rj.bd.lx.lx01;
/*** @desc 发动机类* @author 86186**/
public class Engine implements ICar{@Overridepublic void make() {System.out.println("发动机制作完毕!!");}}
package com.rj.bd.lx.lx01;
/*** @desc 底盘类* @author 86186**/
public class Chassis implements ICar{@Overridepublic void make() {System.out.println("底盘制作完毕!!");}
}
package com.rj.bd.lx.lx01;
/*** @desc 车身类* @author 86186**/
public class Body implements ICar{@Overridepublic void make() {System.out.println("车身制作完毕!!");}
}
简单的工厂类
package com.rj.bd.lx.lx01;public class SimpleCarFactory {public static final int TYPE_FDJ = 1;public static final int TYPE_DP = 2;public static final int TYPE_CS = 3;public static ICar createNoodles(int type) {switch(type) {case TYPE_FDJ:return new Engine();case TYPE_DP:return new Chassis();case TYPE_CS:return new Body();default:return new Body();//默认底盘类}} }
测试类
package com.rj.bd.lx.lx01;public class Test {public static void main(String[] args) {ICar car01 = SimpleCarFactory.createNoodles(SimpleCarFactory.TYPE_FDJ);car01.make();ICar car02 = SimpleCarFactory.createNoodles(SimpleCarFactory.TYPE_DP);car02.make();ICar car03 = SimpleCarFactory.createNoodles(SimpleCarFactory.TYPE_CS);car03.make();System.out.println("全部制造完毕,起飞~");}
}
测试控制台
发动机制作完毕!!
底盘制作完毕!!
车身制作完毕!!
全部制造完毕,起飞~
2.2 小结
- 工厂方法是指定义工厂接口和产品接口,但如何创建实际工厂和实际产品被推迟到子类实现,从而使调用方只和抽象工厂与抽象产品打交道。
- 实际更常用的是更简单的静态工厂方法,它允许工厂内部对创建产品进行优化。
- 调用方尽量持有接口或抽象类,避免持有具体类型的子类,以便工厂方法能随时切换不同的子类返回,却不影响调用方代码。
3. 代理模式
为其他对象提供一种代理以控制对这个对象的访问。
暂不讲述静态代理模式
3.1 动态代理
相比静态代理,动态代理具有更强的灵活性,因为它不用在我们设计实现的时候就指定某一个代理类来代理哪一个被代理对象,我们可以把这种指定延迟到程序运行时由JVM来实现
使用Java动态代理机制的好处:
- 减少编程的工作量:假如需要实现多种代理处理逻辑,只要写多个代理处理器就可以了,无需每种方式都写一个代理类。
- 功能的扩展:系统扩展性和维护性增强,程序修改起来也方便多了(一般只要改代理处理器类就行了)。
动态代理的使用场景:
- 在后期的商业框架中很多都有用到的,例如
Spring
,Struts2
3.2 JDK动态代理
JDK动态代理: JDK动态代理所用到的代理类在程序调用到代理类对象时才由JVM真正创建,JVM根据传进来的 业务实现类对象 以及 方法名 ,动态地创建了一个代理类的class文件并被字节码引擎执行,然后通过该代理类对象进行方法调用。我们需要做的,只需指定代理类的预处理、调用后操作即可
步骤:
- 首先,定义业务逻辑接口
- 然后,实现业务逻辑接口创建业务实现类
- 最后,实现 调用管理接口
InvocationHandler
创建动态代理(工具)类 - 在使用时,首先创建一个业务实现类对象和一个代理类对象,然后定义接口引用(这里使用向上转型)并用代理对象.
bind
(业务实现类对象)的返回值进行赋值。最后通过接口引用调用业务方法即可。(接口引用真正指向的是一个绑定了业务类的代理类对象,所以通过接口方法名调用的是被代理的方法们)
代码举例
接口
package com.rj.bd.lx.lx03;
/*** @desc 租房的接口* @author 86186**/
public interface IHouse {public void renting();
}
租户类
package com.rj.bd.lx.lx03;/*** @desc 租户类* @author 86186**/
public class Tenant implements IHouse{public String rentName;// 租户名public int rent;// 租金public String getRentName() {return rentName;}public void setRentName(String rentName) {this.rentName = rentName;}public int getRent() {return rent;}public void setRent(int rent) {this.rent = rent;}@Overridepublic void renting() {if (rent < 1000) {System.out.println(rentName + "你这" + rent + "块钱租不到房子呀,最少要1000");return;}System.out.println(rentName + "租房花了" + rent);}}
动态代理工具类
package com.rj.bd.lx.lx03;
/*** @desc 动态代理管理类* @author 86186*/
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;public class DymaicProxy implements InvocationHandler {public Object object;// 需要代理的对象public DymaicProxy(Object object) {this.object = object;// 代理租客}@Overridepublic Object invoke(Object arg0, Method arg1, Object[] arg2) throws Throwable {Object resultObject = arg1.invoke(this.object, arg2);return resultObject;}}
测试JDK动态代理模式
package com.rj.bd.lx.lx03;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;/*** @desc 3.用代理模式模拟一个房屋租赁* @author 86186**/
public class Test {public static void main(String[] args) {Tenant tenant = new Tenant();tenant.setRentName("董梦宇");tenant.setRent(3000);InvocationHandler handler = new DymaicProxy(tenant);IHouse house = (IHouse) Proxy.newProxyInstance(handler.getClass().getClassLoader(),tenant.getClass().getInterfaces(), handler);
// System.out.println(handler);house.renting();Tenant tenant01 = new Tenant();tenant01.setRentName("董大宇");tenant01.setRent(500);InvocationHandler handler01 = new DymaicProxy(tenant01);IHouse house01 = (IHouse) Proxy.newProxyInstance(handler01.getClass().getClassLoader(),tenant01.getClass().getInterfaces(), handler01);
// System.out.println(handler01);house01.renting();}
}
测试控制台
董梦宇租房花了3000
董大宇你这500块钱租不到房子呀,最少要1000
3.3 小结
- 代理模式通过封装一个已有接口,并向调用方返回相同的接口类型,能让调用方在不改变任何代码的前提下增强某些功能(例如,鉴权、延迟加载、连接池复用等)。
- 使用Proxy模式要求调用方持有接口,作为Proxy的类也必须实现相同的接口类型。
4. 单例模式
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
单例模式(Singleton
)的目的是为了保证在一个进程中,某个类有且仅有一个实例。
4.1 懒汉式单例与饿汉式单例
- 懒汉式:第一次调用的时候才初始化(线程不安全)
- 饿汉式:类加载的时候就初始化实例
接下来用一段代码来表明懒汉式与饿汉式
懒汉式
package com.rj.bd.lx.lx02;
/*** @desc 懒汉模式* @author 86186**/
public class Insurance {private static Insurance insurance;// 私有无参构造器private Insurance() {}//初始化public static Insurance getInsurance() {if(insurance == null) {insurance new Insurance();}return insurance;}
}
饿汉式
package com.rj.bd.lx.lx02;
/*** @desc 饿汉模式* @author 86186**/
public class Insurance {private static Insurance insurance = new Insurance();// 私有无参构造器private Insurance() {}//初始化public static Insurance getInsurance() {return insurance;}
}
其中懒汉模式是线程不安全的,可以使用synchronized关键字进行同步,但是会浪费很多时间,所以可以在最外面的一层加一个判断,通过双重判断的方式(双重加锁机制),解决效率问题,减少锁的判断次数
如下:
package com.rj.bd.lx.lx02;/*** @desc 懒汉模式* @author 86186**/
public class Insurance {private static Insurance insurance;// 私有无参构造器private Insurance() {}// 初始化public static Insurance getInsurance() {if (insurance == null) {synchronized (Insurance.class) {if (insurance == null) {insurance = new Insurance();}}}return insurance;}
}
代码举例
饿汉保单类
package com.rj.bd.lx.lx02;
/*** @desc 饿汉模式* @author 86186**/
public class Insurance {private static Insurance insurance = new Insurance();// 私有无参构造器private Insurance() {}//初始化public static Insurance getInsurance() {return insurance;}
}
测试类
package com.rj.bd.lx.lx02;
/*** @desc 2.用单例模式模拟一下保险单号* @author 86186**/
public class Test {public static void main(String[] args) {// 模拟一个保险单,一个单只能有一个保险号,如果是其他的类使用,则会出现不同的保险单号,饿汉不会出现线程安全问题for (int i = 0; i < 100; i++) {new Thread(new Runnable() {@Overridepublic void run() {Insurance onlyInsurance = Insurance.getInsurance();System.out.println(onlyInsurance.hashCode());}}).start();}}
}
测试控制台
655160934
655160934
....
655160934
4.2 小结
Singleton
模式是为了保证一个程序的运行期间,某个类有且只有一个全局唯一实例;Singleton
模式既可以严格实现,也可以以约定的方式把普通类视作单例。
5. 策略模式
定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。
5.1 策略模式
策略模式:Strategy
,是指,定义一组算法,并把其封装到一个对象中。然后在运行时,可以灵活的使用其中的一个算法。
话不多说直接上差异代码
不用策略模式:
package com.rj.bd.lx.lx04;import java.security.Identity;
import java.util.Scanner;/*** @desc 模拟一个商场的商品打折促销的小案例: 1)普通用户原价,但是要是满100元减去20元 2)普通会员是,打8折 0)VIP会员打7折* @author 86186**/
public class Test {public static void main(String[] args) {System.out.println("请输入消费金额.....");Scanner cin = new Scanner(System.in);Double d = cin.nextDouble();System.out.println("请输入您的身份,普通用户or普通会员orVIP会员:");String identity = cin.next();if (identity.equals("普通用户")) {if (d >= 100) {System.out.println("您为普通用户,消费为金额" + d + "满减后为" + (d - 20));return;}System.out.println("您为普通用户,消费金额为" + d);} else if (identity.equals("普通会员")) {System.out.println("您为普通会员,消费金额为" + d + ",打8折后价格为" + (d * 0.8));} else if (identity.equals("VIP会员")) {System.out.println("您为VIP会员,消费金额为" + d + ",打7折后价格为" + (d * 0.7));} else {System.out.println("输入错误!");}}
}
控制台输出
请输入消费金额.....
150
请输入您的身份,普通用户or普通会员orVIP会员:
普通会员
您为普通会员,消费金额为150.0,打8折后价格为120.0
使用策略模式
策略接口
package com.rj.bd.lx.lx04;
/*** @desc 策略接口* @author 86186**/
public interface IStrategy {public void identity(double d);
}
普通用户类
package com.rj.bd.lx.lx04;
/*** @desc 普通用户* @author 86186**/
public class OrdinaryUsers implements IStrategy{@Overridepublic void identity(double d) {if (d >= 100) {System.out.println("您为普通用户,消费为金额" + d + "满减后为" + (d - 20));return;}System.out.println("您为普通用户,消费金额为" + d);}}
普通会员类
package com.rj.bd.lx.lx04;
/*** @desc 普通会员* @author 86186**/
public class GeneralMember implements IStrategy{@Overridepublic void identity(double d) {System.out.println("您为普通会员,消费金额为" + d + ",打8折后价格为" + (d * 0.8));}}
VIP会员类
package com.rj.bd.lx.lx04;
/*** @desc VIP会员* @author 86186**/
public class VIPMembers implements IStrategy{@Overridepublic void identity(double d) {System.out.println("您为VIP会员,消费金额为" + d + ",打7折后价格为" + (d * 0.7));}}
策略内容类
package com.rj.bd.lx.lx04;
/*** @desc 策略内容类* @author 86186**/
public class StrategyContext {public IStrategy strategy;//策略内容的构造器public StrategyContext(IStrategy strategy) {super();this.strategy = strategy;}//设置金额public void consumptionAmount(double d) {strategy.identity(d);}
}
测试类
package com.rj.bd.lx.lx04;import java.util.Scanner;/*** @desc 4.用策略模式模拟一个商场的商品打折促销的小案例:1)普通用户原价,但是要是满100元减去20元2)普通会员是,打8折0)VIP会员打7折* @author 86186**/
public class Test {public static void main(String[] args) {System.out.println("请输入消费金额.....");Scanner cin=new Scanner(System.in);Double d = cin.nextDouble();System.out.print("普通用户:");StrategyContext strategyContext01 = new StrategyContext(new OrdinaryUsers());strategyContext01.consumptionAmount(d);System.out.print("会员用户:");StrategyContext strategyContext02 = new StrategyContext(new GeneralMember());strategyContext02.consumptionAmount(d);System.out.print("VIP会员:");StrategyContext strategyContext03 = new StrategyContext(new VIPMembers());strategyContext03.consumptionAmount(d);}
}
测试控制台
请输入消费金额.....
150
普通用户:您为普通用户,消费为金额150.0满减后为130.0
会员用户:您为普通会员,消费金额为150.0,打8折后价格为120.0
VIP会员:您为VIP会员,消费金额为150.0,打7折后价格为105.0
5.2小结
- 策略模式是为了允许调用方选择一个算法,从而通过不同策略实现不同的计算结果。
- 通过扩展策略,不必修改主逻辑,即可获得新策略的结果。
Java常用的设计模式相关推荐
- Java常用的设计模式总结
前言 java设计模式有23种,他们分别是: 创建型模式:单例模式.抽象工厂模式.建造者模式.工厂模式.原型模式. 结构型模式:适配器模式.桥接模式.装饰器模式.组合模式.外观模式.享元模式.代理模式 ...
- java 常用十种设计模式示例归纳 | 已打包请带走
设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结. GitHub地址 DesignPattern 文章说明 一个Demo,集合常用的十种设计模 ...
- 【Java 常用的设计模式】
目录 前言 一.设计原则 1.1 开闭原则 1.2 单一职责原则 1.3 依赖倒置原则 1.4 接口分离原则 1.5 迪米特法则 1.6 里氏替换原则 二.设计模式 2.1 单例模式 2.1.1 饿汉 ...
- java设计模式中不属于创建型模式_23种设计模式第二篇:java工厂模式定义:工厂模式是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式...
23种设计模式第二篇:java工厂模式 定义: 工厂模式是 Java 中最常用的设计模式之一.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式. 工厂模式主要是为创建对象提供过渡接口, ...
- JAVA常用设计模式(一、单例模式、工厂模式)
JAVA设计模式之单例模式 import java.util.HashMap; import java.util.Map;/*** 设计模式之单例模式* 单例模式(Singleton Pattern) ...
- Java常用设计模式————原型模式(一)
介绍 原型模式(Prototype Pattern):用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象. 原型模式用于创建重复的对象,同时又能保证性能.当直接创建对象的代价比较大时,则采用 ...
- Java常用设计模式————工厂模式
简介: 工厂模式(Factory Pattern)是Java中最常用的设计模式之一,又称多态工厂模式.虚拟构造器模式.属于创建型模式. 在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通 ...
- Java开发中常用的设计模式-单例模式
单例(Singleton)模式的定义:指一个类只有一个实例,且该类能自行创建这个实例的一种模式. Java开发中常用的设计模式-单例模式 单例模式有3个特点: 单例类只有一个实例对象: 该单例对象必须 ...
- 初学Java常用设计模式之——原型模式
声明:转载请附上原文链接 提示:标题序号从3开始,是照应不同设计模式笔记发布的顺序而定的,比如,第上一篇文章 初学Java常用设计模式之--工厂模式 序号从2开始. 标题后面之所以加上了解,是因为相对 ...
- java常用设计模式文章收集
java常用设计模式文章收集 java设计模式--接口模式 java设计模式--抽象工厂模式 java设计模式--工厂模式 Java设计模式--迭代器模式 Java设计模式--原型模式 JAVA设计模 ...
最新文章
- [Hadoop in China 2011] 何鹏:Hadoop在海量网页搜索中应用分析
- C#语言学习思维导图
- LOAD_TYPE_VERSION_MISMATCH与TYPELOAD_NEW_VERSION错误分析
- 学习用户连接性要素之连接性(附XMIND整理笔记)
- leetcode 179 python
- java swing container_Java Swing GUI学习(一)
- LinkedHashMap/HashMap 介绍
- chmod 用法示例
- 用data.table语句批量处理变量
- 基于SSM疫情信息爬取与分析系统
- 【转】vista下SMB共享的解决办法
- C# Thread.Sleep 导致内存溢出,在多线程里Sleep的真实情况 软件闪退 内存溢出
- matlab abs()le8,《MATLAB_语言与数学运算》练习答案
- linux c正则
- 前端计算机英语单词,解决文件命名,变量命名头皮发麻,单词超多1700个前端专属单词,css常用命名单词【起名字太烦了全背下来内卷吧小伙伴大前端时代来临了】
- 基于Android的物流管理系统设计与实现(含论文)
- 1/1917钓鱼思维导图
- 【网络安全必看】如何提升自身WEB渗透能力?
- vs写c语言快捷键,vs快捷键
- Xshell连接虚拟机linux
热门文章
- phpcms二次开发摘要
- 高德地图报Native method not found: com.autonavi.amap.mapcore.MapCore.nativeNewInstance:
- Android 高德地图 Native method not found: com.autonavi.amap.mapcore.MapCore.nativeNewInstance:(Ljava/lan
- java手写-txt大文本分割
- vb mysql 教程_VB6 数据库 基础 教程
- 惠普M1005打印机驱动-LaserJet提供下载
- [Mac OS] Homebrew简介及安装wine
- 黑盒测试方法用例设计详解
- tgp dnf服务器文件在哪,DNFTGP补丁使用说明及问题解决方案
- c语言 adt实验报告,Android实验报告