- 面向对象设计的基本原则:
- 创建型模式
- 单例模式 (常用)
- 常见应用场景:
- 常见的五种单例模式实现方式:
- 五种单例模式的代码实现:
- 工厂模式 (常用)
- 工厂模式的分类:
- 应用场景
- 三种模式下的代码实现和UML图:
- 建造者模式 (常用)
- 代码实现和UML图
- 原型模式
- 创建型模式总结
- 结构型模式
- 适配器(adapter)模式 (常用)
- 代理模式 (常用)
- 分类:
- 静态代理(静态定义代理类)
- 动态代理(动态生成代理类):
- 桥接模式(bridge)
- 组合模式(composite)
- 装饰器模式(decorator) (常用)
- 外观模式
- 享元模式(FlyWeight)
- 结构型模式总结:
- 行为型模式
- 责任链模式chain of responsibility (常用)
- 迭代器模式(iterator)
- 中介者模式(Mediator)
- 命令模式(command)(不常用)
- 解释器模式(interpreter)(不常用)
- 访问者模式 (visitor)(不常用)
- 策略模式 (strategy)(常用)
- 模板方法模式 template method (常用)
- 状态模式 (state)
- 观察者模式 (observer)(常用)
- 备忘录模式(memento)
- OCP (开闭原则, Open-Closed Principle) : 一个软件的实体应当对扩展开放,对修改关闭。即当要添加一个新功能时,不要修改已有的类,而是重新写一个类实现这个功能。
- DIP (依赖倒转原则, Dependence Inversion Principle ) : 要针对接口编程,不要针对实现编程。
- LoD (迪米特法则, Law of Demeter) : 只与你直接的朋友通信,而避免和陌生人通信。
单例模式 (常用)
作用:
保证一个类只有一个实例,并且提供一个访问该实例的全局访问点。
常见应用场景:
- Windows的Task Manager (任务管理器)就是很典型的单例模式
- windows的Recycle Bin (回收站)也是典型的单例应用。在整个系统运行过程中,回收站-直维护着仅有的一个实例。
- 项目中,读取配置文件的类, 一般也只有一个对象。 没有必要每次使用配置文件数据,每次new-个对象去读取。
- 网站的计数器, 一般也是采用单例模式实现,否则难以同步。
- 应用程序的日志应用, 一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态 ,因为只能有一个实例去操作,否则内容不好追加。
- 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。
- 操作系统的文件系统,也是大的单例模式实现的具体例子,一个操作系统只能有一个文件系统。
- Application也是单例的典型应用( Servlet编程中会涉及到)
- 在Spring中,每个Bean默认就是单例的,这样做的优点是Spring容器可以管理
在servlet编程中,每个Servlet也是单例 - 在spring MVC框架/struts1框架中,控制器对象也是单例
单例模式的优点:
- 由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决
- 单例模式可以在系统设置全局的访问点 ,优化环共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理
常见的五种单例模式实现方式:
- 主要:
- 饿汉式(线程安全,调用效率高。但是,不能延时加载。)
- 懒汉式(线程安全,调用效率不高。 但是,可以延时加载。);在用的使用才加载。并发效率资源第。
- 其他:
- 双重检测锁式(由于JVM底层内部模型原因,偶尔会出问题。不建议使用);这个模式将同步内容下方到if内部,提高了执行的效率不必每次获取对象时都进行同步,只有第一次才同步创建了以后就没必要了。
- 静态内部类式(线程安全,调用效率高。但是,可以延时加载);也是一种懒加载的方式。兼备了并发高效调用和延迟加载的优势!
- 枚举单例(线程安全,调用效率高,不能延时加载);实现简单,枚举本身就是单例模式。由JVM从根本上提供保障!避免通过反射和反序列化的漏洞!
如何选用
单例对象占用资源少,不需要延时加载:
- 枚举式好于饿汉式
单例对象占用资源大,需要延时加载:
- 静态内部类式好于懒汉式
五种单例模式的代码实现:
饿汉模式:
public class SingletonDemo1 {
//初始化类时,立即加载这个对象(没有延时加载的优势,因为不一定要使用,立即加载可能会浪费资源)
//加载类时,因为创建对象是线程安全的,不需要添加关键字synchronized(加锁),所以饿汉式是天然线程安全的
private static SingletonDemo1 instance = new SingletonDemo1();
//将构造方法私有化
private SingletonDemo1(){
}
//提供一个获得对象的方法,方法没有同步,调用效率高
public static SingletonDemo1 getInstance(){
return instance;
}
}
懒汉式:
public class SingletonDemo2 {
//类初始化时,不立即初始化对象(延时加载,在要使用的时候才进行加载)
private static SingletonDemo2 instance;
//将构造方法私有化
private SingletonDemo2(){
}
//方法同步,调用效率高
public static synchronized SingletonDemo2 getInstance(){
if(instance==null){
instance = new SingletonDemo2();
}
return instance;
}
}
双重检测锁式:
public class SingletonDemo3 {
private static SingletonDemo3 instance = null;
//将构造方法私有化
private SingletonDemo3(){
}
//这个模式将同步内容下方到if内部,提高了执行的效率不必每次获取对象时都进行同步,只有第一次才同步创建了以后就没必要了。
public static SingletonDemo3 getInstance(){
if(instance==null){
SingletonDemo3 sc;
synchronized(SingletonDemo3.class){
sc = instance;
if(sc==null){
synchronized(SingletonDemo3.class){
if(sc==null){
sc = new SingletonDemo3();
}
}
instance = sc;
}
}
}
return instance;
}
}
静态内部类式:
public class SingletonDemo4 {
private static class SingletonClassInstance{
private static final SingletonDemo4 instance = new SingletonDemo4();
}
public static SingletonDemo4 getinstance(){
return SingletonClassInstance.instance;
}
//将构造方法私有化
private SingletonDemo4(){
}
}
枚举式:
public enum SingletonDemo5 {
//这个枚举元素,本事就是单例; 缺点是不能延时加载
INSTANCE;
//添加自己需要的操作
public void singletonOperarion(){
}
}
以上五种单例模式除了枚举式,其他的都有可能会被反射和反序列化破解;下面方式可以防止这种情况的发生。
import java.io.ObjectStreamException;
public class SingletonDemo6 {
//类初始化时,不立即初始化对象(延时加载,在要使用的时候才进行加载)
private static SingletonDemo6 instance;
//将构造方法私有化
private SingletonDemo6(){
//防止通过反射使用私有的构造器
if(instance!=null){
throw new RuntimeException();
}
}
//方法同步,调用效率高
public static synchronized SingletonDemo6 getInstance(){
if(instance==null){
instance = new SingletonDemo6();
}
return instance;
}
//防止通过反序列化破坏单例模式,如果定义了readResolve()则直接返回此方法指定的对象。不需要单独再创建新对象
private Object readResolve() throws ObjectStreamException{
return instance;
}
}
五种单例模式在多线程下的效率情况:
注:数据在不同的环境下可能会有点偏差。
工厂模式 (常用)
作用:
实现了创建者和调用者的分离。
核心本质:
- 实例化对象,用工厂方法代替new操作。
- 将选择实现类、创建对象统一管理和控制。从而将调用者跟我们的实现类解耦。
工厂模式的分类:
- 简单工厂模式:
- 用来生产同一等级结构中的任意产品。 (对于增加新的产品,需要修改已有代码)
- 虽然某种程度不符合设计原则,但实际使用最多。
- 方法模式:
- 用来生产同一等级结构中的固定产品。( 支持增加任意产品);缺点:需要的类可能太多了。
- 不修改已有类的前提下,通过增加新的工厂类实现扩展。
- 抽象工厂模式:
- 用来生产不同产品族的全部产品。( 对于增加新的产品,无能为力;支持增加产品族)
- 不可以增加产品,可以增加产品族
应用场景
- JDK中Calendar的getInstance方法
- JDBC中Connection对象的获取
- Hibernate中SessionFactory创建Session
- spring中IOC容器创建管理bean对象
- XML解析时的documentBuilderFactory创建解析器对象
- 反射中Class对象的newInstance()
三种模式下的代码实现和UML图:
用于测试的类:
public interface Car {
void run();
}
public class Audi implements Car{
@Override
public void run() {
System.out.println("奥迪在跑");
}
}
public class Byd implements Car{
@Override
public void run() {
System.out.println("比亚迪在跑");
}
}
简单工厂实现:
public class CarFactory1 {
public static Car createAudi(){
return new Audi();
}
public static Car createByd(){
return new Byd();
}
}
测试类:
public class Client02 {
public static void main(String[] args) {
Car c1 = CarFactory1.createAudi();
Car c2 = CarFactory1.createByd();
c1.run();
c2.run();
}
}
简单工厂下的UML图:
方法模式工厂:
工厂方法模式要点:
- 为了避免简单工厂模式的缺点,不完全满足OCP。
- 工厂方法模式和简单工厂模式最大的不同在于,简单工厂模式只有一个(对于一个项目或者一个独立模块而言)工厂类,而工厂方法模式有一组实现了相同接口的工厂类。
- 每一种产品都有一个工厂类对应,当需要添加产品时,只需要添加新的类即可,不需要修改原来的代码。
代码实现:
接口:
public interface CarFactory {
Car createCar();
}
奥迪工厂:
public class AudiFactory implements CarFactory{
@Override
public Car createCar() {
return new Audi();
}
}
比亚迪工厂:
public class BydFactory implements CarFactory{
@Override
public Car createCar() {
return new Byd();
}
}
测试类:
public class Client03 {
public static void main(String[] args) {
Car c1 = new AudiFactory().createCar();
Car c2 = new BydFactory().createCar();
c1.run();
c2.run();
}
}
方法工厂的UML图:
抽象工厂模式:
作用:
- 用来生产不同产品族的全部产品。( 对于增加新的产品,无能为力;支持增加产品族)
- 抽象工厂模式是工厂方法模式的升级版本,在有多个业务品种、业务分类时,通过抽象工厂模式产生需要的对象是一种非常好的解决方式。
代码实现:
引擎产品:
package abstractFactory;
public interface Engine {
void run();
void start();
}
//高端引擎
class LuxuryEngine implements Engine{
@Override
public void run() {
System.out.println("转的快!");
}
@Override
public void start() {
System.out.println("启动快!可以自动暂停");
}
}
//低端引擎
class LowEngine implements Engine{
@Override
public void run() {
System.out.println("转的慢!");
}
@Override
public void start() {
System.out.println("启动慢!");
}
}
座椅产品:
package abstractFactory;
public interface Seat {
void massage();
}
//高端座椅
class LuxurySeat implements Seat{
@Override
public void massage() {
System.out.println("可以自动按摩");
}
}
//低端座椅
class LowSeat implements Seat{
@Override
public void massage() {
System.out.println("不能自动按摩");
}
}
轮胎产品:
package abstractFactory;
//轮胎接口
public interface Tyre {
void revolve();
}
//高端轮胎
class LuxuryTyre implements Tyre{
@Override
public void revolve() {
System.out.println("旋转不磨损");
}
}
//低端轮胎
class LowTyre implements Tyre{
@Override
public void revolve() {
System.out.println("旋转磨损");
}
}
工厂接口:
package abstractFactory;
public interface CarFactory {
Engine CreateEngine();
Seat CreateSeat();
Tyre CreateTre();
}
高端产品族工厂:
package abstractFactory;
public class LuxuryCarFactory implements CarFactory{
@Override
public Engine CreateEngine() {
return new LuxuryEngine();
}
@Override
public Seat CreateSeat() {
return new LuxurySeat();
}
@Override
public Tyre CreateTre() {
return new LuxuryTyre();
}
}
低端产品族工厂:
package abstractFactory;
public class LowCarFactory implements CarFactory{
@Override
public Engine CreateEngine() {
return new LowEngine();
}
@Override
public Seat CreateSeat() {
return new LowSeat();
}
@Override
public Tyre CreateTre() {
return new LowTyre();
}
}
测试类:
package abstractFactory;
public class Client {
public static void main(String[] args) {
CarFactory factory = new LuxuryCarFactory();
Engine e = factory.CreateEngine();
e.run();
e.start();
}
}
建造者模式 (常用)
应用场景:
- 我们要建造一个复杂的产品。比如:神州飞船,Iphone。这个复杂的产品的创建。有这样个问题需要处理:装配这些子组件是不是有个步骤问题?
- 实际开发中,我们所需要的对象构建时,也非常复杂,有很多步骤需要处理时。
建造模式的本质:
- 分离了对象子组件的单独构造(由Builder来负责)和装配(由Director负责)。从而可以构造出复杂的对象。这个模式适用于:某个对象的构建过程复杂的情况下使用。
- 由于实现了构建和装配的解耦。不同的构建器,相同的装配,也可以做出不同的对象;相同的构建器,不同的装配顺序也可以做出不同的对象。也就是实现了构建算法、装配算法的解耦,实现了更好的复用。
开发中应用场景:
- StringBuilder类的append方法
- SQL中的PreparedStatement
- JDOM中, DomBuilder、SAXBuilder
飞船和零件类:
package 建造者模式;
public class AirShip {
private OrbitalModule orbitalModule; //轨道舱
private Engine engine; //发动机
private EscapeTower escapeTower; //逃逸塔
public void launch(){
System.out.println("发射!");
}
public OrbitalModule getOrbitalModule() {
return orbitalModule;
}
public void setOrbitalModule(OrbitalModule orbitalModule) {
this.orbitalModule = orbitalModule;
}
public Engine getEngine() {
return engine;
}
public void setEngine(Engine engine) {
this.engine = engine;
}
public EscapeTower getEscapeTower() {
return escapeTower;
}
public void setEscapeTower(EscapeTower escapeTower) {
this.escapeTower = escapeTower;
}
}
class OrbitalModule {
private String name;
public OrbitalModule(String name) {
super();
this.name = name;
}
}
class Engine {
private String name;
public Engine(String naem) {
super();
this.name= naem;
}
public String getName() {
return name;
}
}
class EscapeTower {
private String name;
public EscapeTower(String name) {
super();
this.name = name;
}
}
建造者接口:
public interface AirShipBuilder {
Engine builderEngine();
OrbitalModule builderOrbitaModule();
EscapeTower builderEscapeTower();
}
组装者接口:
public interface AirShipDirector {
//组装飞船对象
AirShip directAirShip();
}
构建者类:
package 建造者模式;
public class SxtAirShipBuilder implements AirShipBuilder{
@Override
public Engine builderEngine() {
System.out.println("构建发动机");
return new Engine("发动机");
}
@Override
public OrbitalModule builderOrbitaModule() {
System.out.println("构建逃逸塔");
return new OrbitalModule("逃逸塔");
}
@Override
public EscapeTower builderEscapeTower() {
System.out.println("构建轨道舱");
return new EscapeTower("轨道舱");
}
}
组装者类:
package 建造者模式;
public class SxtAirshipDirector implements AirShipDirector{
private AirShipBuilder builder;
public SxtAirshipDirector(AirShipBuilder builder){
this.builder = builder;
}
@Override //将组件组装成飞船
public AirShip directAirShip() {
Engine e = builder.builderEngine();
OrbitalModule o = builder.builderOrbitaModule();
EscapeTower et = builder.builderEscapeTower();
//装配成飞船对象
AirShip ship = new AirShip();
ship.setEngine(e);
ship.setEscapeTower(et);
ship.setOrbitalModule(o);
return ship;
}
}
测试类:
public class Client {
public static void main(String[] args) {
AirShipDirector director = new SxtAirshipDirector(new SxtAirShipBuilder());
AirShip ship = director.directAirShip();
System.out.println(ship.getEngine().getName());
ship.launch();
}
}
UML图:
原型模式
原型模式介绍:
- 通过new产生一 个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
- 就是java中的克隆技术,以某个对象为原型,复制出新的对象。显然,新的对象具备原型对象的特点
- 优势有:效率高(直接克隆,避免了重新执行构造过程步骤)。
- 克隆类似于new,但是不同于new。new创建新的对象属性采用的是默认值。克隆出的对象的属性值完全和原型对象相同。并且克隆出的新对象改变不会影响原型对象。然后,再修改克隆对象的值。
原型模式实现:
- Cloneable接口和clone方法
- Prototype(原型)模式中实现起来最困难的地方就是内存复制操作 ,所幸在Java中提供了clone()方法替我们做了绝大部分事情。
开发中的应用场景
- 原型模式很少单独出现,-般是和工厂方法模式一起出现,通过clone的方法创建一个对象 ,然后由工厂方法提供给调用者。
- spring中bean的创建实际就是两种:单例模式和原型模式。( 当然,原型模式需要和工厂模式搭配起来)
代码实现:
package 原型模式;
import java.sql.Date;
public class Sheep implements Cloneable{
private String sname;
private Date birthday;
//重写克隆方法
protected Object clone() throws CloneNotSupportedException{
Object obj = super.clone(); //直接调用Object对象的clone()方法
//添加如下代码实现深克隆(deep Clone);如果没有进行深坑,克隆出来的属性会指向同一个对象
Sheep s = (Sheep) obj;
s.birthday = (Date) this.birthday.clone(); //把属性也进行克隆
return obj;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public Sheep() {
}
public Sheep(String sname, Date birthday) {
super();
this.sname = sname;
this.birthday = birthday;
}
}
测试类:
package 原型模式;
import java.sql.Date;
public class Client {
public static void main(String[] args) throws Exception {
Date d = new Date(123456567L);
Sheep s1 = new Sheep("shao利", d);
System.out.println(s1);
System.out.println(s1.getSname());
System.out.println(s1.getBirthday());
Sheep s2 = (Sheep)s1.clone();
d.setTime(666666666666L);
s2.setSname("多利");
System.out.println(s2);
System.out.println(s2.getSname());
System.out.println(s2.getBirthday());
}
}
创建型模式总结
创建型模式:都是用来帮助我们创建对象的!
单例模式:
- 保证一个类只有一个实例,并且提供一 个访问该实例的全局访问点。
工厂模式:
- 简单工厂模式:
用来生产同一等级结构中的任意产品。( 对于增加新的产品,需要修改已有代码) - 工厂方法模式:
用来生产同一等级结构中的固定产品。( 支持增加任意产品) - 抽象工厂模式:
一用来生产不同产品族的全部产品。( 对于增加新的产品,无能为力;支持增加产品族)
建造者模式:
- 分离了对象子组件的单独构造(由Builder来负责)和装配(由Director负责)。从而可以构造出复杂的对象。
- 通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式
原型模式:
- 通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式
结构型模式
核心作用:
是从程序的结构,上实现松耦合,从而可以扩大整体的类结构,用来解决更大的问题。
结构型模式分类:
- 适配器模式
- 代理模式
- 桥接模式
- 装饰模式
- 组合模式
- 外观模式
- 享元模式
适配器(adapter)模式 (常用)
什么是适配器模式?
- 将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作。
模式中的角色
- 目标接口( Target) :客户所期待的接口。目标可以是具体的或抽象的类,也可以是接口。
- 需要适配的类( Adaptee) : 需要适配的类或适配者类。
- 适配器( Adapter) :通过包装一个需要适配的对象,把原接口转换成目标接口。
使用场景:
- 经常用来做旧系统改造和升级
- java.io.InputStreamReader(InputStream)
- java.io.OutputStreamWriter(OutputStream)
代码实现:
被适配的类:
public class Adaptee {
public void request(){
System.out.println("可以完成客户请求需要的功能");
}
}
接口:
public interface Target {
void handleReq();
}
适配器:
public class Adapter extends Adaptee implements Target{
@Override
public void handleReq() {
super.request();
}
}
客户端:
public class Client {
public void test1(Target t){
t.handleReq();
}
public static void main(String[] args) {
Client c = new Client();
Adaptee a = new Adaptee();
Target t = new Adapter();
//通过适配器来使用其他类的方法
c.test1(t);
}
}
代理模式 (常用)
作用:
- 通过代理,控制对对象的访问;可以详细控制访问某个(某类)对象的方法,在调用这个方法前做前置处理,调用这个方法后做后置处理。( 即: AOP的微观实现! )
- AOP(Aspect Oriented Programming面向切面编程)的核心实现机制 !
核心角色:
- 抽象角色:
- 定义代理角色和真实角色的公共对外方法
- 真实角色:
- 实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。关注真正的业务逻辑!
- 代理角色:
- 实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
- 将统一的流程控制放到代理角色中处理!
应用场景:
- 安全代理:屏蔽对真实角色的直接访问。
- 远程代理:通过代理类处理远程方法调用(RMI)
- 延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象。(比如你要开发一个大文档查看软件,大文档中有大的图片,有可能个图片有100MB ,在打开文件时不可能将所有的图片都显示出来,这样就可以使用代理模式。当需要查看图片时。用proxy来进行大图片的打开。)
开发框架中应用场景:
- struts2中拦截器的实现
- 数据库连接池关闭处理
- Hibernate中延时加载的实现
- mybatis中实现拦截器插件
- AspectJ的实现
- spring中AOP的实现
- 日志拦截
- 声明式事务处理
- web service
- RMI远程方法调用
代码实现:
抽象类:
public interface Star {
void confer(); //面谈
void signContract(); //签合同
void bookTicket(); //订票
void sing(); //唱歌
void collectMoney(); //收钱
}
真实角色:
public class RealStar implements Star{
@Override
public void confer() {
System.out.println("RealStar.confer()");
}
@Override
public void signContract() {
System.out.println("RealStar.signContract()");
}
@Override
public void bookTicket() {
System.out.println("RealStar.bookTicket()");
}
@Override
public void sing() {
System.out.println("真实角色实现唱歌功能RealStar.sing()");
}
@Override
public void collectMoney() {
System.out.println("RealStar.collectMoney()");
}
}
代理角色:
public class ProxyStar implements Star{
private Star star;
public ProxyStar(Star real) {
super();
this.star = real;
}
public ProxyStar(){
}
@Override
public void confer() {
System.out.println("ProxyStar.confer()");
}
@Override
public void signContract() {
System.out.println("ProxyStar.signContract()");
}
@Override
public void bookTicket() {
System.out.println("ProxyStar.bookTicket()");
}
//代理角色不能实现核心功能:唱歌,必须通过star对象调用
public void sing() {
star.sing();
}
@Override
public void collectMoney() {
System.out.println("ProxyStar.bookTicket()");
}
}
测试类:
public class Client {
public static void main(String[] args) {
Star real = new RealStar();
Star proxy = new ProxyStar(real); //对真实角色进行代理
proxy.confer();
proxy.signContract();
proxy.bookTicket();
proxy.signContract();
proxy.sing();
proxy.collectMoney();
}
}
UML图:
1.JDK自带的动态代理
- java.lang.reflest.Proxy.
作用:动态生成代理类和对象 - java,lang.reflect.InvocationHandler(处理器接口)
- 可以通过invoke方法实现对真文角色的代理访问。
- 每次通过Proxy生成代理类对象对象时都要指定对应的处理器对象
动态代理:在内存中形成代理类
-
实现步骤:
- 代理对象和真实对象实现相同的接口
- 代理对象 = Proxy.newProxyInstance();
- 使用代理对象调用方法。
- 增强方法
-
增强方式:
- 增强参数列表
- 增强返回值类型
- 增强方法体执行逻辑
下面用代理卖电脑的方式进行讲解:
接口:
public interface SaleComputer {
public String sale(double money);
public void show();
}
真实类:
package cn.itcast.proxy;
public class Lenovo implements SaleComputer {
@Override
public String sale(double money) {
System.out.println("花了"+money+"元买了一台联想电脑...");
return "联想电脑";
}
@Override
public void show() {
System.out.println("展示电脑....");
}
}
实现动态代理:
package cn.itcast.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyTest {
public static void main(String[] args) {
//1.创建真实对象
Lenovo lenovo = new Lenovo();
//2.动态代理增强lenovo对象
SaleComputer proxy_lenovo = (SaleComputer) Proxy.newProxyInstance(lenovo.getClass().getClassLoader(), lenovo.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//判断是否是sale方法
if(method.getName().equals("sale")){
//1.增强参数
double money = (double) args[0];
money = money * 0.85;
System.out.println("专车接你....");
//使用真实对象调用该方法
String obj = (String) method.invoke(lenovo, money);
System.out.println("免费送货...");
//2.增强返回值
return obj+"_鼠标垫";
}else{
Object obj = method.invoke(lenovo, args);
return obj;
}
}
});
//3.调用方法
proxy_lenovo.show();
}
}
2.javaassist字节码操作库实现
3.CGLIB
4.ASM(底层使用指令,可维护性较差)
桥接模式(bridge)
为了方便理解桥接模式解决的问题,下面引入一个场景:
商城系统中常见的商品分类.以电脑为类,如何良好的处理商品分类销售的问题?
我们可以用多层继承结构实现下图的关系。
多层继承结构带来的问题:
- 扩展性问题(类个数膨胀问题) :
- 如果要增加一个新的电脑类型:智能手机,则要增加各个品牌下面的类。如果要增加一个新的品牌,也要增加各种电脑类型的类。
- 违反单一职责原则:
- 一个类:联想笔记本,有两个引起这个类变化的原因(品牌和机型)
场景分析:
- 商城系统中常见的商品分类,以电脑为类,如何良好的处理商品分类销售的问题?
- 这个场景中有两个变化的维度:电脑类型、电脑品牌。
- 我们可以按照这两个维度作为划分,每一个维度建立一系列的类;然后将两个维度结构桥接起来(用组合代替继承)。当需要添加品牌和机型时,在对应的维度添加类即可。
桥接模式核心要点:
- 处理多层继承结构,处理多维度变化的场景,将各个维度设计成独立的继承结构,使各个维度可以独立的扩展在抽象层建立关联。
桥接模式实际开发中应用场景:
-
JDBC驱动程序。
-
AWT中的Peer架构
-
银行日志管理:
- 格式分类:操作日志、交易日志、异常日志
- 距离分类:本地记录日志、异地记录日志
-
人力资源系统中的奖金计算模块:
- 奖金分类:个人奖金、团体奖金、激励奖金。
- 部门分类:人事部门、销售部门、研发部门。
-
OA系统中的消息处理:
- 业务类型:普通消息、加急消息、特急消息
- 发送消息方式:系统内消息、手机短信、邮件
代码实现:
品牌维度:
package 桥接模式;
public interface Brand {
void sale();
}
class Lenovo implements Brand{
@Override
public void sale() {
System.out.println("销售联想电脑");
}
}
class Dell implements Brand{
@Override
public void sale() {
System.out.println("销售Dell电脑");
}
}
class Shenzhou implements Brand{
@Override
public void sale() {
System.out.println("销售Shenzhou电脑");
}
}
机型维度:
package 桥接模式;
public class Computer {
protected Brand brand;
public Computer(Brand b){
this.brand = b;
}
public void sale(){
brand.sale();
}
}
class Desktop extends Computer{
public Desktop(Brand b) {
super(b);
}
public void sale(){
super.sale();
System.out.println("销售台式电脑");
}
}
class Laptop extends Computer{
public Laptop(Brand b) {
super(b);
}
public void sale(){
super.sale();
System.out.println("销售笔记本电脑");
}
}
测试类:
public class Client {
public static void main(String[] args) {
//销售联想的笔记本电脑
Computer c = new Laptop(new Lenovo());
c.sale();
//销售神舟的台式机
Computer c2 = new Desktop(new Shenzhou());
c2.sale();
}
}
桥接模式总结:
- 桥接模式可以取代多层继承的方案。多层继承违背了单一职责原则,复用性较差,类的个数也非常多。桥接模式可以极大的减少子类的个数,从而降低管理和维护的成本。
- 桥接模式极大的提高了系统可扩展性,在两个变化维度中任意扩展一个维度,都不需要修改原有的系统,符合开闭原则。
组合模式(composite)
使用组合模式的场景:
- 把部分和整体的关系用树形结构来表示,从而使客户端可以使用统一的方式处理部分对象和整体对象。(比如吧文件夹看做一个特殊的文件)
组合模式核心:
- 抽象构件(Component)角色:定义了叶子和容器构件的共同点
- 叶子(Leaf)构件角色:无子节点
- 容器(Composite)构件角色:有容器特征,可以包含子节点
开发中的应用场景:
- 操作系统的资源管理器
- GUI中的容器层次图
- XML文件解析
- 0A系统中,组织结构的处理
- Junit单元测试框架
底层设计就是典型的组合模式, TestCase(叶子)、TestUnite(容器)、Test接口(抽象)
常见代码形式:
package 组合模式;
public interface Component {
void operation();
}
//叶子组件
interface Leaf extends Component{
}
//容器组件
interface Composite extends Component{
void add(Component c);
void remove(Component c);
Component getChild(int index);
}
下面模拟一个杀毒软件,文件作为叶子,文件夹作为容器。代码如下:
package 组合模式;
import java.util.ArrayList;
import java.util.List;
//抽象构建
public interface AbstractFile {
void killVirus(); //杀毒
}
//图片文件
class ImageFile implements AbstractFile{
private String name;
public ImageFile(String name) {
super();
this.name = name;
}
@Override
public void killVirus() {
System.out.println("-----图像文件"+name+"进行查杀");
}
}
//文本文件
class TextFile implements AbstractFile{
private String name;
public TextFile(String name) {
super();
this.name = name;
}
@Override
public void killVirus() {
System.out.println("-----文本文件"+name+"进行查杀");
}
}
//视频文件
class VideoFile implements AbstractFile{
private String name;
public VideoFile(String name) {
super();
this.name = name;
}
@Override
public void killVirus() {
System.out.println("-----视频文件"+name+"进行查杀");
}
}
//文件夹,
class Folder implements AbstractFile{
private String name;
//定义容器,用来存放本容器下的子节点
private List list = new ArrayList();
public Folder(String name) {
super();
this.name = name;
}
public void add(AbstractFile file){
list.add(file);
}
public void remove(AbstractFile file){
list.remove(file);
}
public AbstractFile getChild(int index){
return list.get(index);
}
@Override
public void killVirus() {
System.out.println("---文件夹:"+name+"进行查杀");
for(AbstractFile file:list){
file.killVirus();
}
}
}
测试类:
package 组合模式;
public class Client {
public static void main(String[] args) {
AbstractFile f2, f3, f4, f5;
Folder f1 = new Folder("我的收藏");
f2 = new ImageFile("大头照.jpg");
f3 = new TextFile("Hello.txt");
f1.add(f2);
f1.add(f3);
Folder f11 = new Folder("电影");
f4 = new VideoFile("笑傲江湖.avi");
f5 = new VideoFile("神雕侠侣.avi");
f11.add(f4);
f11.add(f5);
f1.add(f11);
f1.killVirus();
}
}
测试结果:
---文件夹:我的收藏进行查杀 -----图像文件大头照.jpg进行查杀 -----文本文件Hello.txt进行查杀 ---文件夹:电影进行查杀 -----视频文件笑傲江湖.avi进行查杀 -----视频文件神雕侠侣.avi进行查杀
装饰器模式(decorator) (常用)
作用:
- 动态的为一个对象增加新的功能。
- 装饰模式是一种用于代替继承的技术,无须通过继承增加子类就能扩展对象的新功能。使用对象的关联关系代替继承关系,更加灵活,同时避免类型体系的快速膨胀。
开发中使用的场景:
- IO中输入流和输出流的设计
- Swing包中图形界面构件功能
- Servlet API中提供了一个request对象的Decorator设计模式的默认实现类HttpServletRequestWrapper ,HttpServletRequestWrapper类,增强了request对象的功能。
- Struts2中 ,request,response,session对象的处理
实现细节:
- Component抽象构件角色:
真实对象和装饰对象有相同的接口。这样,客户端对象就能够以与真实对象相同的方式同装饰对象交互。 - ConcreteComponent 具体构件角色(真实对象) :
io流中的FileInputStream、 FileOutputStream - Decorator装饰角色 :
持有一个抽象构件的引用。装饰对象接受所有客户端的请求,并把这些请求转发给真实的对象。这样,就能在真实对象调用前后增加新的功能。 - ConcreteDecorator具体装饰角色:
负责给构件对象增加新的责任。
优点:
- 扩展对象功能,比继承灵活,不会导致类个数急剧增加
- 可以对一个对象进行多次装饰,创造出不同行为的组合,得到功能更
加强大的对象 - 具体构建类和具体装饰类可以独立变化,用户可以根据需要自己增加新的具体构件子类和具体装饰子类。
缺点:
- 产生很多小对象。大量小对象占据内存, 一定程度上影响性能。
- 装饰模式易于出错,调试排查比较麻烦。
装饰模式和桥接模式的区别:
- 两个模式都是为了解决过多子类对象问题。但他们的诱因不一样。桥模式是对象自身现有机制沿着多个维度变化,是既有部分不稳定。装饰模式是为了增加新的功能。
下面通过给汽车添加新功能,体现装饰模式:
代码实现:
package 装饰模式;
public interface ICar {
void move();
}
// ConcreteComponent 具体构件角色(真实对象)
class Car implements ICar {
@Override
public void move() {
System.out.println("陆地上跑!");
}
}
//Decorator装饰角色
class SuperCar implements ICar{
private ICar car;
public SuperCar(ICar car) {
super();
this.car = car;
}
@Override
public void move() {
car.move();
}
}
//ConcreteDecorator具体装饰角色
class FlyCar implements ICar{
private ICar car;
public FlyCar(ICar car) {
super();
this.car = car;
}
public void fly(){
System.out.println("天上飞");
}
@Override
public void move() {
car.move();
fly();
}
}
//ConcreteDecorator具体装饰角色
class WaterCar implements ICar{
private ICar car;
public WaterCar(ICar car) {
super();
this.car = car;
}
public void wsim(){
System.out.println("水上游! ");
}
@Override
public void move() {
car.move();
wsim();
}
}
//ConcreteDecorator具体装饰角色
class AICar implements ICar{
private ICar car;
public AICar(ICar car) {
super();
this.car = car;
}
public void autoMove(){
System.out.println("自动驾驶");
}
@Override
public void move() {
car.move();
autoMove();
}
}
测试类:
public class Client {
public static void main(String[] args) {
Car car = new Car();
car.move();
System.out.println("增加新的功能,飞行----------" );
FlyCar flycar = new FlyCar(car);
flycar.move();
System.out.println("增加新的功能,水里游--------");
WaterCar waterCar = new WaterCar(car);
waterCar.move();
System.out.println("增加两个新的功能,飞行,水里游----------");
WaterCar waterCar2 = new WaterCar(new FlyCar(car));
waterCar2.move();
}
}
UML图:
外观模式
迪米特法则(最少知识原则) :
- 一个软件实体应当尽可能少的与其他实体发生相互作用。
外观模式核心:
- 为子系统提供统一的入口。封装子系统的复杂性,便于客户端调用。
开发中常见的场景:
频率很高。哪里都会遇到。各种技术和框架中,都
有外观模式的使用。如:
- JDBC封装后的, commons提供的DBUtils类,
- Hibernate提供的工具类、Spring JDBC工具类等
下面以注册公司流程进行举例:
不使用外观模式的情况:
使用外观模式后:
享元模式(FlyWeight)
使用场景:
- 内存属于稀缺资源,不要随便浪费。如果有很多个完全相同或相似的对象,我们可以通过享元模式,节省内存(时间换空间)。
核心:
- 享元模式以共享的方式高效地支持大量细粒度对象的重用。
- 享元对象能做到共享的关键是区分了内部状态和外部状态。
- 内部状态:可以共享,不会随环境变化而改变
- 外部状态:不可以共享,会随环境变化而改变
- 比如围棋中的每一个棋子都是一个对象,它们的具有相同的属性:颜色、形状、大小,这些事可以共享的,称之为内部状态;但是每一个棋子的位置是不一样的,这是不可以共享的,称之为外部状态。
享元模式实现:
- FlyweightFactory享元工厂 类
创建并管理享元对象,享元池一般设计成键值对 - FlyWeight抽象享元类
通常是一个接口或抽象类,声明公共方法,这些方法可以向外界提供对象的内部状态,设置外部状态。 - ConcreteFlyWeight具体享元类
, 为为部状态提供成员变量进行存储 - UnsharedConcreteFlyWeight非共享享元类
不能被共享的子类可以设计为非共享享元类
享元模式开发中应用的场景:
- 元模式由于其共享的特性,可以在任何“池”中操作
比如:线程池、数据库连接池。 - String类的设计也是享元模式
优点:
- 极大减少内存中对象的数量
- 相同或相似对象内存中只存一-份 ,极大的节约资源,提高系统性能
- 外部状态相对独立,不影响内部状态
缺点:
- 模式较复杂,使程序逻辑复杂化
- 为了节省内存,共享了内部状态,分离出外部状态,而读取外部状态使运行时间变长。用时间换取了空间。
下面按照棋子关系展示代码的实现:
package 享元模式;
public interface ChessFlyWeight {
void setColor();
String getColor();
void display(Coordinate c);
}
//具体享元类, 为为部状态提供成员变量进行存储
class ConcreateChess implements ChessFlyWeight{
private String color;
public ConcreateChess(String color) {
super();
this.color = color;
}
@Override
public void setColor() {
}
@Override
public String getColor() {
return color;
}
@Override
public void display(Coordinate c) {
}
}
坐标类:
package 享元模式;
public class Coordinate {
private int x, y;
public Coordinate(int x, int y) {
super();
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
享元工厂:
package 享元模式;
import java.util.HashMap;
import java.util.Map;
public class ChessFlyWeightFactory {
//享元池
private static Map map = new HashMap();
//获得棋子
public static ChessFlyWeight getChess(String color){
if(map.get(color)!=null){
return map.get(color);
}else{
ChessFlyWeight cfw = new ConcreateChess(color);
map.put(color, cfw);
return cfw;
}
}
}
测试类:
public class Client {
public static void main(String[] args) {
ChessFlyWeight chess1 = ChessFlyWeightFactory.getChess("黑色");
ChessFlyWeight chess2 = ChessFlyWeightFactory.getChess("黑色");
System.out.println(chess1);
System.out.println(chess2);
System.out.println("增加外部状态的处理===============");
chess1.display(new Coordinate(10, 10));
chess2.display(new Coordinate(10, 10));
}
}
UML图:
- 结构型模式关注对象和类的组织。
行为型模式关注系统中对象之间的相互交互,研究系统在运行时对象之间的相互通信和协作进一步明确对象的职责,共有11种模式:
- 责任链模式 chain of responsibility
- 命令模式 command(不常用)
- 解释器模式 interpreter(不常用)
- 迭代器模式 iterator
- 中介者模式 mediator
- 备忘录模式 memento
- 观察者模式 observer
- 状态模式 state
- 策略模式 strategy
- 模板方法模式 template method
- 访问者模式 visitor(不常用)
责任链模式chain of responsibility (常用)
定义:
- 将能够处理同一类请求的对象连成一条链,所提交的请求沿着链传递,链上的对象逐个判断是否有能力处理该请求,如果能则处理,如果不能则传递给链上的下一个对象。
开发中常见的场景:
- Java中,异常机制就是一种责任链模式。一个try可以对应多 个catch ,当第一个catch不匹配类型,则自动跳到第二个catch.
- Javascript语言中, 事件的冒泡和捕获机制。Java语言中,事件的处理采用观察者模式。
- Servlet开发中,过滤器的链式处理
- Struts2中 ,拦截器的调用也是典型的责任链模式
模拟场景:
公司里面,请假条的审批过程:
- 如果请假天数小于3天,主任审批
- 如果请假天数大于等手3天,小于10天,经理审批
- 如果大于等于10天,小于30天,总经理审批
- 如果大于等于30天,提示拒绝
代码实现:
请假条类:
package 责任链模式;
public class LeaveRequest {
private String empName;
private int leaveDays;
private String reason;
public LeaveRequest(String empName, int leaveDays, String reason) {
super();
this.empName = empName;
this.leaveDays = leaveDays;
this.reason = reason;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public int getLeaveDays() {
return leaveDays;
}
public void setLeaveDays(int leaveDays) {
this.leaveDays = leaveDays;
}
public String getReason() {
return reason;
}
public void setReason(String reason) {
this.reason = reason;
}
}
抽象类:
package 责任链模式;
public abstract class Leader {
protected String name;
protected Leader nextLeader; //责任链的后继
public Leader(String name) {
super();
this.name = name;
}
//设定责任链上的后继对象
public void setNextLeader(Leader nextLeader) {
this.nextLeader = nextLeader;
}
//处理请求的核心业务方法
public abstract void handleRequest(LeaveRequest request);
}
主任类:
public class Director extends Leader{
public Director(String name) {
super(name);
}
@Override
public void handleRequest(LeaveRequest request) {
if(request.getLeaveDays()<3){
System.out.println("员工:"+request.getEmpName()+"请假,天数:"+request.getLeaveDays()+",理由:"+request.getReason());
System.out.println("主任:"+name+" 审批通过!");
}else{
if(this.nextLeader!=null){
this.nextLeader.handleRequest(request);
}
}
}
}
经理类:
public class Manager extends Leader{
public Manager(String name) {
super(name);
}
@Override
public void handleRequest(LeaveRequest request) {
if(request.getLeaveDays()<10){
System.out.println("员工:"+request.getEmpName()+"请假,天数:"+request.getLeaveDays()+",理由:"+request.getReason());
System.out.println("经理:"+name+" 审批通过!");
}else{
if(this.nextLeader!=null){
this.nextLeader.handleRequest(request);
}
}
}
}
总经理类:
public class GeneralManager extends Leader{
public GeneralManager(String name) {
super(name);
}
@Override
public void handleRequest(LeaveRequest request) {
if(request.getLeaveDays()<30){
System.out.println("员工:"+request.getEmpName()+"请假,天数:"+request.getLeaveDays()+",理由:"+request.getReason());
System.out.println("总经理:"+name+" 审批通过!");
}else{
System.out.println("请假天数超过30天,拒绝审批!");
}
}
}
测试类:
public class Client {
public static void main(String[] args) {
Leader a = new Director("张三");
Leader b = new Manager("李四");
Leader c = new GeneralManager("王五");
//组织责任链对象的关系
a.setNextLeader(b);
a.setNextLeader(c);
//开始请假操作
LeaveRequest req1 = new LeaveRequest("TOM", 1, "回家探亲!");
a.handleRequest(req1);
LeaveRequest req2 = new LeaveRequest("TOM", 5, "回家探亲!");
a.handleRequest(req2);
LeaveRequest req3 = new LeaveRequest("TOM", 15, "回家探亲!");
a.handleRequest(req3);
}
}
测试结果:
员工:TOM请假,天数:1,理由:回家探亲!
主任:张三 审批通过!
员工:TOM请假,天数:5,理由:回家探亲!
总经理:王五 审批通过!
员工:TOM请假,天数:15,理由:回家探亲!
总经理:王五 审批通过!
迭代器模式(iterator)
场景:
- 提供一种可以遍历聚合对象的方式。又称为:游标cursor模式
- 聚合对象:存储数据
- 迭代器:遍历数据
结构:
- 聚合对象:存储数据
- 迭代器:遍历数据
开发中常见的场景:
- JDK内置的迭代器(List/Set)
代码实现:
接口定义:
package 迭代器模式;
public interface MyIterator {
void first(); // 将游标指向第一个元素
void next(); // 将游标指向下一个元素
boolean hasNext(); // 判断是否存在下一个元素
boolean isFirst();
boolean isLast();
Object getCurrentObje(); //获取当前游标指向的对象
}
用内部类实现迭代器:
package 迭代器模式;
import java.util.ArrayList;
import java.util.List;
public class ConcreteMyAggregate {
private List


