同步操作将从 康小庄/设计模式代码 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
学习地址
https://www.bilibili.com/video/BV1G4411c7N4
https://www.bilibili.com/video/BV1Np4y1z7BU
参考文章
http://c.biancheng.net/view/1317.html
"设计模式"最初并不是出现在软件设计中,而是被用于建筑领域的设计中。
1977年美国著名建筑大师、加利福尼亚大学伯克利分校环境结构中心主任克里斯托夫·亚历山大(Christopher Alexander)
在他的著作《建筑模式语言:城镇、建筑、构造》中描述了一些常见的建筑设计问题,并提出了 253 种关于对城镇、邻里、住宅、花园和房间等进行设计的基本模式。
1990年软件工程界开始研讨设计模式的话题,后来召开了多次关于设计模式的研讨会。直到1995 年,艾瑞克·伽马(ErichGamma)、理査德·海尔姆(Richard Helm)、拉尔夫·约翰森(Ralph Johnson)、约翰·威利斯迪斯(John Vlissides)等 4 位作者合作出版了《设计模式:可复用面向对象软件的基础》一书,在此书中收录了 23 个设计模式,这是设计模式领域里程碑的事件,导致了软件设计模式的突破。这 4 位作者在软件开发领域里也以他们的“四人组”(Gang of Four,GoF)著称。
软件设计模式(Software Design Pattern),又称设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。它描述了在软件设计过程中的一些不断重复发生的问题,以及该问题的解决方案。也就是说,它是解决特定问题的一系列套路,是前辈们的代码设计经验的总结,具有一定的普遍性,可以反复使用。
设计模式的本质是面向对象设计原则的实际运用,是对类的封装性、继承性和多态性以及类的关联关系和组合关系的充分理解。
正确使用设计模式具有以下优点。
创建型模式
用于描述“怎样创建对象”,它的主要特点是“将对象的创建与使用分离”。GoF(四人组)书中提供了单例、原型、工厂方法、抽象工厂、建造者等 5 种创建型模式。
结构型模式
用于描述如何将类或对象按某种布局组成更大的结构,GoF(四人组)书中提供了代理、适配器、桥接、装饰、外观、享元、组合等 7 种结构型模式。
行为型模式
用于描述类或对象之间怎样相互协作共同完成单个对象无法单独完成的任务,以及怎样分配职责。GoF(四人组)书中提供了模板方法、策略、命令、职责链、状态、观察者、中介者、迭代器、访问者、备忘录、解释器等 11 种行为型模式。
统一建模语言(Unified Modeling Language,UML)是用来设计软件的可视化建模语言。它的特点是简单、统一、图形化、能表达软件设计中的动态与静态信息。
UML 从目标系统的不同角度出发,定义了用例图、类图、对象图、状态图、活动图、时序图、协作图、构件图、部署图等 9 种图。
类图(Class diagram)是显示了模型的静态结构,特别是模型中存在的类、类的内部结构以及它们与其他类的关系等。类图不显示暂时性的信息。类图是面向对象建模的主要组成部分。
在UML类图中,类使用包含类名、属性(field) 和方法(method) 且带有分割线的矩形来表示,比如下图表示一个Employee类,它包含name,age和address这3个属性,以及work()方法。
属性/方法名称前加的加号和减号表示了这个属性/方法的可见性,UML类图中表示可见性的符号有三种:
+:表示public
-:表示private
#:表示protected
属性的完整表示方式是: 可见性 名称 :类型 [ = 缺省值]
方法的完整表示方式是: 可见性 名称(参数列表) [ : 返回类型]
注意:
1,中括号中的内容表示是可选的
2,也有将类型放在变量名前面,返回值类型放在方法名前面
举个栗子:
上图Demo类定义了三个方法:
关联关系是对象之间的一种引用关系,用于表示一类对象与另一类对象之间的联系,如老师和学生、师傅和徒弟、丈夫和妻子等。关联关系是类与类之间最常用的一种关系,分为一般关联关系、聚合关系和组合关系。我们先介绍一般关联。
关联又可以分为单向关联,双向关联,自关联。
1,单向关联
在UML类图中单向关联用一个带箭头的实线表示。上图表示每个顾客都有一个地址,这通过让Customer类持有一个类型为Address的成员变量类实现。
2,双向关联
从上图中我们很容易看出,所谓的双向关联就是双方各自持有对方类型的成员变量。
在UML类图中,双向关联用一个不带箭头的直线表示。上图中在Customer类中维护一个List<Product>,表示一个顾客可以购买多个商品;在Product类中维护一个Customer类型的成员变量表示这个产品被哪个顾客所购买。
3,自关联
自关联在UML类图中用一个带有箭头且指向自身的线表示。上图的意思就是Node类包含类型为Node的成员变量,也就是“自己包含自己”。
聚合关系是关联关系的一种,是强关联关系,是整体和部分之间的关系。
聚合关系也是通过成员对象来实现的,其中成员对象是整体对象的一部分,但是成员对象可以脱离整体对象而独立存在。例如,学校与老师的关系,学校包含老师,但如果学校停办了,老师依然存在。
在 UML 类图中,聚合关系可以用带空心菱形的实线来表示,菱形指向整体。下图所示是大学和教师的关系图:
组合表示类之间的整体与部分的关系,但它是一种更强烈的聚合关系。
在组合关系中,整体对象可以控制部分对象的生命周期,一旦整体对象不存在,部分对象也将不存在,部分对象不能脱离整体对象而存在。例如,头和嘴的关系,没有了头,嘴也就不存在了。
在 UML 类图中,组合关系用带实心菱形的实线来表示,菱形指向整体。下图所示是头和嘴的关系图:
依赖关系是一种使用关系,它是对象之间耦合度最弱的一种关联方式,是临时性的关联。在代码中,某个类的方法通过局部变量、方法的参数或者对静态方法的调用来访问另一个类(被依赖类)中的某些方法来完成一些职责。
在 UML 类图中,依赖关系使用带箭头的虚线来表示,箭头从使用类指向被依赖的类。下图所示是司机和汽车的关系图,司机驾驶汽车:
继承关系是对象之间耦合度最大的一种关系,表示一般与特殊的关系,是父类与子类之间的关系,是一种继承关系。
在 UML 类图中,泛化关系用带空心三角箭头的实线来表示,箭头从子类指向父类。在代码实现时,使用面向对象的继承机制来实现泛化关系。例如,Student 类和 Teacher 类都是 Person 类的子类,其类图如下图所示:
实现关系是接口与实现类之间的关系。在这种关系中,类实现了接口,类中的操作实现了接口中所声明的所有的抽象操作。
在 UML 类图中,实现关系使用带空心三角箭头的虚线来表示,箭头从实现类指向接口。例如,汽车和船实现了交通工具,其类图如图 9 所示。
在软件开发中,为了提高软件系统的可维护性和可复用性,增加软件的可扩展性和灵活性,程序员要尽量根据6条原则来开发程序,从而提高软件开发效率、节约软件开发成本和维护成本。
对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级。
想要达到这样的效果,我们需要使用接口和抽象类。
因为抽象灵活性好,适应性广,只要抽象的合理,可以基本保持软件架构的稳定。而软件中易变的细节可以从抽象派生来的实现类来进行扩展,当软件需要发生变化时,只需要根据需求重新派生一个实现类来扩展就可以了。
里氏代换原则是面向对象设计的基本原则之一。
里氏代换原则:任何基类可以出现的地方,子类一定可以出现。通俗理解:子类可以扩展父类的功能,但不能改变父类原有的功能。换句话说,子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。
如果通过重写父类的方法来完成新的功能,这样写起来虽然简单,但是整个继承体系的可复用性会比较差,特别是运用多态比较频繁时,程序运行出错的概率会非常大。
里氏代换原则错误示范
package com.zhuang.principle.liskov;
/**
* @Classname Liskov
* @Description 里氏代换原则错误示范
* @Date 2021/3/15 13:58
* @Created by dell
*/
public class Liskov {
public static void main(String[] args) {
A a = new A();
System.out.println("11-3=" +a.fun1(11,3));
System.out.println("11-8=" +a.fun1(11,8));
System.out.println("===================");
B b = new B();
System.out.println("11-3="+b.fun1(11,3));
System.out.println("1-8="+b.fun1(1,8));
System.out.println("11+3+9="+b.fun2(11,3));
}
}
class A{
//返回两个数的差
public int fun1(int num1,int num2){
return num1-num2;
}
}
//B类继承A 增加新功能,完成两个数相加,然后和9求和
class B extends A{
@Override
public int fun1(int a, int b) {
return a+b;
}
public int fun2(int a, int b) {
return fun1(a,b)+9;
}
}
里氏代换原则正确示范
package com.zhuang.principle.liskov;
/**
* @Classname Liskov2
* @Description 里氏代换原则
* @Date 2021/3/15 14:13
* @Created by dell
*/
public class Liskov2 {
public static void main(String[] args) {
Base base = new Base();
base.add(5,6);
base.sub(6,2);
Sub sub = new Sub();
sub.mul(5,6);
sub.div(10,2);
}
}
class Base {
//通用加法运算
public void add(int a, int b) {
System.out.println(a + "+" + b + "=" + (a + b));
}
//通用减法运算
public void sub(int a, int b) {
System.out.println(a + "-" + b + "=" + (a - b));
}
}
class Sub extends Base {
//子类特有乘法运算
public void mul(int a, int b) {
System.out.println(a + "*" + b + "=" + (a * b));
}
//子类特有除法运算
public void div(int a, int b) {
System.out.println(a + "/" + b + "=" + (a / b));
}
}
高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。
依赖倒转原则错误示范
package com.zhuang.principle.inversion;
/**
* @Classname DependenceInversion1
* @Description 依赖倒转原则错误示范
* @Date 2021/3/15 13:20
* @Created by dell
*/
public class DependenceInversion1 {
public static void main(String[] args) {
Person person = new Person();
person.receive(new Email());
person.receive(new WeiXin());
}
}
//定义接口
interface IReceiver{
public String getInfo();
}
class WeiXin implements IReceiver{
@Override
public String getInfo() {
return "发送微信消息...";
}
}
class Email implements IReceiver{
@Override
public String getInfo() {
return "发送邮件消息...";
}
}
//对接口的依赖
class Person{
public void receive(IReceiver receiver){
System.out.println(receiver.getInfo());
}
}
依赖倒转原则正确示范
package com.zhuang.principle.inversion;
/**
* @Classname DependenceInversion2
* @Description 依赖倒转原则正确示范
* @Date 2021/3/15 13:27
* @Created by dell
*/
public class DependenceInversion2 {
public static void main(String[] args) {
Client client = new Client();
client.receive(new Emailiml());
client.receive(new WXimpl());
}
}
interface IReceive{
public void printInfo(Integer uid);
}
class WXimpl implements IReceive {
@Override
public void printInfo(Integer uid) {
System.out.println("发送微信消息"+uid);
}
}
class Emailiml implements IReceive {
@Override
public void printInfo(Integer uid) {
System.out.println("发送邮件信息"+uid);
}
}
class Client{
public void receive(IReceive receive){
receive.printInfo(12345);
}
}
面向对象的开发很好的解决了这个问题,一般情况下抽象的变化概率很小,让用户程序依赖于抽象,实现的细节也依赖于抽象。即使实现细节不断变动,只要抽象不变,客户程序就不需要变化。这大大降低了客户程序与实现细节的耦合度。
客户端不应该被迫依赖于它不使用的方法;一个类对另一个类的依赖应该建立在最小的接口上。
接口隔离原则
package com.zhuang.principle.segregation;
/**
* @Classname Sergregation
* @Description 接口隔离原则
* @Date 2021/3/15 13:02
* @Created by dell
*/
public class Sergregation {
public static void main(String[] args) {
C c = new C();
c.depend1(new A());
c.depend2(new A());//C类通过接口去依赖A类
c.depend3(new A());
System.out.println("=======================");
D d = new D();
d.depend1(new B());
d.depend4(new B());//D类通过接口去依赖B类
d.depend5(new B());
}
}
interface interface1{
void operation1();
}
interface interface2{
void operation2();
void operation3();
}
interface interface3{
void operation4();
void operation5();
}
class A implements interface1,interface2{
@Override
public void operation1() {
System.out.println("A 实现了operation1.....");
}
@Override
public void operation2() {
System.out.println("A 实现了operation2......");
}
@Override
public void operation3() {
System.out.println("A 实现了operation3......");
}
}
class B implements interface1,interface3{
@Override
public void operation1() {
System.out.println("B 实现了operation1.....");
}
@Override
public void operation4() {
System.out.println("B 实现了operation4.....");
}
@Override
public void operation5() {
System.out.println("B 实现了operation5.....");
}
}
//C类通过接口interface1,interface2依赖使用A类 只会使用到1,2,3方法
class C{
public void depend1(interface1 i){
i.operation1();
}
public void depend2(interface2 i){
i.operation2();
}
public void depend3(interface2 i){
i.operation3();
}
}
//D类通过接口interface1,interface3 依赖使用B类,用到1,4,5方法
class D{
public void depend1(interface1 i){
i.operation1();
}
public void depend4(interface3 i){
i.operation4();
}
public void depend5(interface3 i){
i.operation5();
}
}
迪米特法则又叫最少知识原则。
只和你的直接朋友交谈,不跟“陌生人”说话(Talk only to your immediate friends and not to strangers)。
其含义是:如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性。
迪米特法则中的“朋友”是指:当前对象本身、当前对象的成员对象、当前对象所创建的对象、当前对象的方法参数等,这些对象同当前对象存在关联、聚合或组合关系,可以直接访问这些对象的方法。
下面看一个例子来理解迪米特法则
【例】明星与经纪人的关系实例
明星由于全身心投入艺术,所以许多日常事务由经纪人负责处理,如和粉丝的见面会,和媒体公司的业务洽淡等。这里的经纪人是明星的朋友,而粉丝和媒体公司是陌生人,所以适合使用迪米特法则。
类图如下:
代码如下:
明星类(Star)
public class Star {
private String name;
public Star(String name) {
this.name=name;
}
public String getName() {
return name;
}
}
粉丝类(Fans)
public class Fans {
private String name;
public Fans(String name) {
this.name=name;
}
public String getName() {
return name;
}
}
媒体公司类(Company)
public class Company {
private String name;
public Company(String name) {
this.name=name;
}
public String getName() {
return name;
}
}
经纪人类(Agent)
public class Agent {
private Star star;
private Fans fans;
private Company company;
public void setStar(Star star) {
this.star = star;
}
public void setFans(Fans fans) {
this.fans = fans;
}
public void setCompany(Company company) {
this.company = company;
}
public void meeting() {
System.out.println(fans.getName() + "与明星" + star.getName() + "见面了。");
}
public void business() {
System.out.println(company.getName() + "与明星" + star.getName() + "洽淡业务。");
}
}
合成复用原则是指:尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。
通常类的复用分为继承复用和合成复用两种。
继承复用虽然有简单和易实现的优点,但它也存在以下缺点:
采用组合或聚合复用时,可以将已有对象纳入新对象中,使之成为新对象的一部分,新对象可以调用已有对象的功能,它有以下优点:
下面看一个例子来理解合成复用原则
【例】汽车分类管理程序
汽车按“动力源”划分可分为汽油汽车、电动汽车等;按“颜色”划分可分为白色汽车、黑色汽车和红色汽车等。如果同时考虑这两种分类,其组合就很多。类图如下:
从上面类图我们可以看到使用继承复用产生了很多子类,如果现在又有新的动力源或者新的颜色的话,就需要再定义新的类。我们试着将继承复用改为聚合复用看一下。
创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是“将对象的创建与使用分离”。
这样可以降低系统的耦合度,使用者不需要关注对象的创建细节。
创建型模式分为:
**单例(Singleton)模式的定义:**指一个类只有一个实例,且该类能自行创建这个实例的一种模式。例如,Windows 中只能打开一个任务管理器,这样可以避免因打开多个任务管理器窗口而造成内存资源的浪费,或出现各个窗口显示内容的不一致等错误。
在计算机系统中,还有 Windows 的回收站、操作系统中的文件系统、多线程中的线程池、显卡的驱动程序对象、打印机的后台处理服务、应用程序的日志对象、数据库的连接池、网站的计数器、Web 应用的配置对象、应用程序中的对话框、系统中的缓存等常常被设计成单例。
单例模式在现实生活中的应用也非常广泛,例如公司 CEO、部门经理等都属于单例模型。J2EE 标准中的 ServletgContext 和 ServletContextConfig、Spring 框架应用中的 ApplicationContext、数据库中的连接池等也都是单例模式。
单例模式有 3 个特点:
单例模式的优点:
单例模式的缺点:
单例设计模式分类两种:
饿汉式:类加载就会导致该单实例对象被创建
懒汉式:类加载不会导致该单实例对象被创建,而是首次使用该对象时才会创建
饿汉式(静态变量)
package com.zhuang.singleton.type1;
/**
* @Classname SingletonTest01
* @Description 饿汉式(静态变量)
* @Date 2021/3/17 9:26
* @Created by dell
*/
public class SingletonTest01 {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
//判断是否为单例
System.out.println(instance == instance2);
System.out.println("intstance的哈希值" + instance.hashCode());
System.out.println("intstance2的哈希值" + instance2.hashCode());
}
}
class Singleton {
//1.构造器私有化,外部能new、
private Singleton() {
}
//本类内部创建对象实例
private final static Singleton instance = new Singleton();
//对外部提供一个公有的静态方法
public static Singleton getInstance() {
return instance;
}
}
说明:
该方式在成员位置声明Singleton类型的静态变量,并创建Singleton类的对象instance。instance对象是随着类的加载而创建的。如果该对象足够大的话,而一直没有使用就会造成内存的浪费。
静态代码块
package com.zhuang.singleton.type2;
/**
* @Classname SingletonTest02
* @Description 静态代码块
* @Date 2021/3/17 9:35
* @Created by dell
*/
public class SingletonTest02 {
public static void main(String[] args) {
Singleton2 instance = Singleton2.getInstance();
Singleton2 instance2 = Singleton2.getInstance();
//判断是否为单例
System.out.println(instance == instance2);
System.out.println("intstance的哈希值" + instance.hashCode());
System.out.println("intstance2的哈希值" + instance2.hashCode());
}
}
class Singleton2 {
//1.构造器私有化,外部能new、
private Singleton2() {
}
//本类内部创建对象实例
private static Singleton2 instance;
/*
在静态代码块中创建对象
*/
static {
instance = new Singleton2();
}
//对外部提供一个公有的静态方法
public static Singleton2 getInstance() {
return instance;
}
}
说明:
该方式在成员位置声明Singleton类型的静态变量,而对象的创建是在静态代码块中,也是对着类的加载而创建。所以和饿汉式的方式1基本上一样,当然该方式也存在内存浪费问题。
懒汉式 线程不安全
package com.zhuang.singleton.type3;
/**
* @Classname SingletonTest03
* @Description 懒汉式 线程不安全
* @Date 2021/3/17 9:39
* @Created by dell
*/
public class SingletonTest03 {
public static void main(String[] args) {
System.out.println("懒汉式,线程不安全!!!");
Singleton instance = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
//判断是否为单例
System.out.println(instance == instance2);
System.out.println("intstance的哈希值" + instance.hashCode());
System.out.println("intstance2的哈希值" + instance2.hashCode());
}
}
class Singleton {
private static Singleton instance;
private Singleton() {
}
//提供一个静态的公有方法 当使用到该方法时,才去创建instance
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
说明:
从上面代码我们可以看出该方式在成员位置声明Singleton类型的静态变量,并没有进行对象的赋值操作,那么什么时候赋值的呢?当调用getInstance()方法获取Singleton类的对象的时候才创建Singleton类的对象,这样就实现了懒加载的效果。但是,如果是多线程环境,会出现线程安全问题。
懒汉式(线程安全 , 同步方法)
package com.zhuang.singleton.type4;
/**
* @Classname SingletonTest04
* @Description 懒汉式(线程安全 , 同步方法)
* @Date 2021/3/17 9:46
* @Created by dell
*/
public class SingletonTest04 {
public static void main(String[] args) {
System.out.println("懒汉式,线程安全!!!");
Singleton instance = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
//判断是否为单例
System.out.println(instance == instance2);
System.out.println("intstance的哈希值" + instance.hashCode());
System.out.println("intstance2的哈希值" + instance2.hashCode());
}
}
class Singleton {
private static Singleton instance;
private Singleton() {
}
//提供一个静态的公有方法,加入同步处理的代码,解决线程安全问题
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
说明:
该方式也实现了懒加载效果,同时又解决了线程安全问题。但是在getInstance()方法上添加了synchronized关键字,导致该方法的执行效果特别低。从上面代码我们可以看出,其实就是在初始化instance的时候才会出现线程安全问题,一旦初始化完成就不存在了。
懒汉式(线程安全 , 同步代码块)
package com.zhuang.singleton.type5;
/**
* @Classname SingletonTest05
* @Description 懒汉式(线程安全 , 同步代码块)
* @Date 2021/3/17 9:50
* @Created by dell
*/
public class SingletonTest05 {
public static void main(String[] args) {
System.out.println("懒汉式,线程安全!,同步代码块");
Singleton instance = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
//判断是否为单例
System.out.println(instance == instance2);
System.out.println("intstance的哈希值" + instance.hashCode());
System.out.println("intstance2的哈希值" + instance2.hashCode());
}
}
class Singleton {
private static Singleton instance;
private Singleton() {
}
//提供一个静态的公有方法,加入同步处理的代码,解决线程安全问题
public static synchronized Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
instance = new Singleton();
}
}
return instance;
}
}
双重检查锁模式是一种非常好的单例实现模式,解决了单例、性能、线程安全问题,上面的双重检测锁模式看上去完美无缺,其实是存在问题,在多线程的情况下,可能会出现空指针问题,出现问题的原因是JVM在实例化对象的时候会进行优化和指令重排序操作。
要解决双重检查锁模式带来空指针异常的问题,只需要使用 volatile
关键字, volatile
关键字可以保证可见性和有序性。
package com.zhuang.singleton.type6;
/**
* @Classname SingletonTest06
* @Description 双重检查,推荐使用
* @Date 2021/3/17 9:54
* @Created by dell
*/
public class SingletonTest06 {
public static void main(String[] args) {
System.out.println("懒汉式,双重检查,推荐使用");
Singleton instance = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
//判断是否为单例
System.out.println(instance == instance2);
System.out.println("intstance的哈希值" + instance.hashCode());
System.out.println("intstance2的哈希值" + instance2.hashCode());
}
}
class Singleton {
private static volatile Singleton instance;
private Singleton() {
}
//提供一个静态的公有方法,加入双重检查代码,加入同步处理的代码,解决懒加载的问题
//保证效率。推荐使用
public static synchronized Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
instance = new Singleton();
}
}
return instance;
}
}
小结:
添加 volatile
关键字之后的双重检查锁模式是一种比较好的单例实现模式,能够保证在多线程的情况下线程安全也不会有性能问题。
静态内部类实现单例模式!
package com.zhuang.singleton.type7;
/**
* @Classname SingletonTest07
* @Description 静态内部类实现单例模式!
* @Date 2021/3/17 9:59
* @Created by dell
*/
public class SingletonTest07 {
public static void main(String[] args) {
System.out.println("静态内部类实现单例模式");
Singleton instance = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
//判断是否为单例
System.out.println(instance == instance2);
System.out.println("intstance的哈希值" + instance.hashCode());
System.out.println("intstance2的哈希值" + instance2.hashCode());
}
}
class Singleton {
private static Singleton instance;
private Singleton() {
}
//写一个静态内部类,该类中有个静态属性,Singleton
public static class SingletonInstance {
private static final Singleton INSTANCE = new Singleton();
}
//提供一个静态的公有方法,直接返回SingletonInstance.INSTANCE;
public static synchronized Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}
说明:
第一次加载Singleton类时不会去初始化INSTANCE,只有第一次调用getInstance,虚拟机加载SingletonHolder
并初始化INSTANCE,这样不仅能确保线程安全,也能保证 Singleton 类的唯一性。
小结:
静态内部类单例模式是一种优秀的单例模式,是开源项目中比较常用的一种单例模式。在没有加任何锁的情况下,保证了多线程下的安全,并且没有任何性能影响和空间的浪费。
枚举的方式实现单例模式
package com.zhuang.singleton.type8;
/**
* @Classname SingletonTest08
* @Description 枚举的方式实现单例模式
* @Date 2021/3/17 10:06
* @Created by dell
*/
public class SingletonTest08 {
public static void main(String[] args) {
System.out.println("枚举的方式实现单例模式,推荐使用");
Singleton instance = Singleton.INSTANCE;
Singleton instance2 = Singleton.INSTANCE;
System.out.println(instance == instance2);
//判断是否为单例
System.out.println(instance == instance2);
System.out.println("intstance的哈希值" + instance.hashCode());
System.out.println("intstance2的哈希值" + instance2.hashCode());
}
}
/*
枚举
*/
enum Singleton {
INSTANCE;//属性
public void method() {
System.out.println("method()方法被调用...");
}
}
说明:
枚举方式属于恶汉式方式。
破坏单例模式:
使上面定义的单例类(Singleton)可以创建多个对象,枚举方式除外。有两种方式,分别是序列化和反射。
序列化反序列化
Singleton类:
public class Singleton implements Serializable {
//私有构造方法
private Singleton() {}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
//对外提供静态方法获取该对象
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
Test类:
public class Test {
public static void main(String[] args) throws Exception {
//往文件中写对象
//writeObject2File();
//从文件中读取对象
Singleton s1 = readObjectFromFile();
Singleton s2 = readObjectFromFile();
//判断两个反序列化后的对象是否是同一个对象
System.out.println(s1 == s2);
}
private static Singleton readObjectFromFile() throws Exception {
//创建对象输入流对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\Users\\dell\\Desktop\\a.txt"));
//第一个读取Singleton对象
Singleton instance = (Singleton) ois.readObject();
return instance;
}
public static void writeObject2File() throws Exception {
//获取Singleton类的对象
Singleton instance = Singleton.getInstance();
//创建对象输出流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\dell\\Desktop\\a.txt"));
//将instance对象写出到文件中
oos.writeObject(instance);
}
}
上面代码运行结果是
false
,表明序列化和反序列化已经破坏了单例设计模式。
反射
Singleton类:
public class Singleton {
//私有构造方法
private Singleton() {}
private static volatile Singleton instance;
//对外提供静态方法获取该对象
public static Singleton getInstance() {
if(instance != null) {
return instance;
}
synchronized (Singleton.class) {
if(instance != null) {
return instance;
}
instance = new Singleton();
return instance;
}
}
}
Test类:
public class Test {
public static void main(String[] args) throws Exception {
//获取Singleton类的字节码对象
Class clazz = Singleton.class;
//获取Singleton类的私有无参构造方法对象
Constructor constructor = clazz.getDeclaredConstructor();
//取消访问检查
constructor.setAccessible(true);
//创建Singleton类的对象s1
Singleton s1 = (Singleton) constructor.newInstance();
//创建Singleton类的对象s2
Singleton s2 = (Singleton) constructor.newInstance();
//判断通过反射创建的两个Singleton对象是否是同一个对象
System.out.println(s1 == s2);
}
}
上面代码运行结果是
false
,表明序列化和反序列化已经破坏了单例设计模式
注意:枚举方式不会出现这两个问题。
问题的解决
序列化、反序列方式破坏单例模式的解决方法
在Singleton类中添加readResolve()
方法,在反序列化时被反射调用,如果定义了这个方法,就返回这个方法的值,如果没有定义,则返回新new出来的对象。
Singleton类:
public class Singleton implements Serializable {
//私有构造方法
private Singleton() {}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
//对外提供静态方法获取该对象
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
/**
* 下面是为了解决序列化反序列化破解单例模式
*/
private Object readResolve() {
return SingletonHolder.INSTANCE;
}
}
源码解析:
ObjectInputStream类
public final Object readObject() throws IOException, ClassNotFoundException{
...
// if nested read, passHandle contains handle of enclosing object
int outerHandle = passHandle;
try {
Object obj = readObject0(false);//重点查看readObject0方法
.....
}
private Object readObject0(boolean unshared) throws IOException {
...
try {
switch (tc) {
...
case TC_OBJECT:
return checkResolve(readOrdinaryObject(unshared));//重点查看readOrdinaryObject方法
...
}
} finally {
depth--;
bin.setBlockDataMode(oldMode);
}
}
private Object readOrdinaryObject(boolean unshared) throws IOException {
...
//isInstantiable 返回true,执行 desc.newInstance(),通过反射创建新的单例类,
obj = desc.isInstantiable() ? desc.newInstance() : null;
...
// 在Singleton类中添加 readResolve 方法后 desc.hasReadResolveMethod() 方法执行结果为true
if (obj != null && handles.lookupException(passHandle) == null && desc.hasReadResolveMethod()) {
// 通过反射调用 Singleton 类中的 readResolve 方法,将返回值赋值给rep变量
// 这样多次调用ObjectInputStream类中的readObject方法,继而就会调用我们定义的readResolve方法,所以返回的是同一个对象。
Object rep = desc.invokeReadResolve(obj);
...
}
return obj;
}
反射方式破解单例的解决方法
public class Singleton {
//私有构造方法
private Singleton() {
/*
反射破解单例模式需要添加的代码
*/
if(instance != null) {
throw new RuntimeException();
}
}
private static volatile Singleton instance;
//对外提供静态方法获取该对象
public static Singleton getInstance() {
if(instance != null) {
return instance;
}
synchronized (Singleton.class) {
if(instance != null) {
return instance;
}
instance = new Singleton();
return instance;
}
}
}
说明:
这种方式比较好理解。当通过反射方式调用构造方法进行创建创建时,直接抛异常。不运行此中操作。
Runtime类就是使用的单例设计模式。
通过源代码查看使用的是哪儿种单例模式
public class Runtime {
private static Runtime currentRuntime = new Runtime();
/**
* Returns the runtime object associated with the current Java application.
* Most of the methods of class <code>Runtime</code> are instance
* methods and must be invoked with respect to the current runtime object.
*
* @return the <code>Runtime</code> object associated with the current
* Java application.
*/
public static Runtime getRuntime() {
return currentRuntime;
}
/** Don't let anyone else instantiate this class */
private Runtime() {}
...
}
从上面源代码中可以看出Runtime类使用的是恶汉式(静态属性)方式来实现单例模式的。
使用Runtime类中的方法
public class RuntimeDemo {
public static void main(String[] args) throws IOException {
//获取Runtime类对象
Runtime runtime = Runtime.getRuntime();
//返回 Java 虚拟机中的内存总量。
System.out.println(runtime.totalMemory());
//返回 Java 虚拟机试图使用的最大内存量。
System.out.println(runtime.maxMemory());
//创建一个新的进程执行指定的字符串命令,返回进程对象
Process process = runtime.exec("ipconfig");
//获取命令执行后的结果,通过输入流获取
InputStream inputStream = process.getInputStream();
byte[] arr = new byte[1024 * 1024* 100];
int b = inputStream.read(arr);
System.out.println(new String(arr,0,b,"gbk"));
}
}
原型(Prototype)模式的定义如下:**用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。在这里,原型实例指定了要创建的对象的种类。**用这种方式创建对象非常高效,根本无须知道对象创建的细节。例如,Windows 操作系统的安装通常较耗时,如果复制就快了很多。
原型模式的优点:
原型模式的缺点:
原型模式包含以下主要角色。
浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。
深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。
IdCard
package com.zhuang.prototype.shallowclone;
/**
* @Classname IdCard
* @Description 浅拷贝的示例
* @Date 2021/3/19 12:16
* @Created by dell
*/
public class IdCard {
private String id;
public IdCard(String id) {
this.id = id;
}
@Override
public String toString() {
return "IdCard{" +
"id=" + id +
'}';
}
}
Person
package com.zhuang.prototype.shallowclone;
/**
* @Classname Person
* @Description 浅拷贝的示例
* @Date 2021/3/19 12:17
* @Created by dell
*/
public class Person implements Cloneable {
private String name;
private int age;
private IdCard idCard;
public Person() {
}
public Person(String name, int age, IdCard idCard) {
this.name = name;
this.age = age;
this.idCard = idCard;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public IdCard getIdCard() {
return idCard;
}
public void setIdCard(IdCard idCard) {
this.idCard = idCard;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", idCard=" + idCard + ", idCard.hashCode=" + idCard.hashCode() +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
PersonTest
package com.zhuang.prototype.shallowclone;
/**
* @Classname PersonTest
* @Description 浅拷贝测试类
* @Date 2021/3/19 12:17
* @Created by dell
*/
public class PersonTest {
public static void main(String[] args) throws Exception {
Person person = new Person("张三", 20, new IdCard("10086"));
Person person1 = (Person) person.clone();
Person person2 = (Person) person.clone();
System.out.println(person);
System.out.println(person1);
System.out.println(person2);
}
}
我们发现可以通过实现implements Cloneable
来完成浅拷贝,基本变量是值传递克隆,而引用对象IdCard
则是引用传递,这不符合我们面向对象思想,每一个Person
应该都有一个独立的IdCard
,而不是共用一个,而要解决这种问题,我们需要使用深克隆
IdCard
package com.zhuang.prototype.deepclone.one;
/**
* @Classname IdCard
* @Description 深克隆的示例
* @Date 2021/3/19 12:33
* @Created by dell
*/
//实现Cloneable接口
public class IdCard implements Cloneable {
private String id;
public IdCard(String id) {
this.id = id;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Override
public String toString() {
return "IdCard{" +
"id='" + id + '\'' +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
Person
package com.zhuang.prototype.deepclone.one;
/**
* @Classname Person
* @Description 深克隆的示例
* @Date 2021/3/19 12:33
* @Created by dell
*/
public class Person implements Cloneable {
private String name;
private int age;
private IdCard idCard;
public Person(String name, int age, IdCard idCard) {
this.name = name;
this.age = age;
this.idCard = idCard;
}
public IdCard getIdCard() {
return idCard;
}
public void setIdCard(IdCard idCard) {
this.idCard = idCard;
}
@Override
public String toString() {
return "Person{" +
"personHashCode=" + this.hashCode() +
", name='" + name + '\'' +
", age=" + age +
", idCard=" + idCard +
", idCardHashCode=" + idCard.hashCode() +
'}';
}
//深克隆需要自己手动实现,在对象引用中也要实现clone方法
@Override
protected Object clone() throws CloneNotSupportedException {
//完成基本数据类型的拷贝
//通过new关键字创建的对象是引用类型
Object person = super.clone();
//对引用类型单独处理
Person p = (Person) person;
IdCard idCard = (IdCard) p.getIdCard().clone(); //实现自己的克隆
p.setIdCard(idCard);
return p;
}
}
PersonTest
package com.zhuang.prototype.deepclone.one;
import java.io.Serializable;
/**
* @Classname PersonTest
* @Description 深克隆测试类
* @Date 2021/3/19 12:33
* @Created by dell
*/
public class PersonTest implements Serializable {
public static void main(String[] args) throws Exception {
Person person = new Person("张三", 20, new IdCard("10086"));
Person person1 = (Person) person.clone();
Person person2 = (Person) person.clone();
System.out.println(person);
System.out.println(person1);
System.out.println(person2);
}
}
使用这种深克隆的方式,完美的解决了当数据类型为引用类型时,只是拷贝原引用对象地址而不是一个全新的引用对象的引用,但是这种实现有一个很大的弊端,需要在每一个对象中都实现clone方法,如果类全是你自己写的,那自然没问题,实现一下就行了,不过有点麻烦。但是,如果你引用的是第三方的一个类,无法修改源代码,这种方式,显然就无法实现深克隆了
IdCard
package com.zhuang.prototype.deepclone.two;
/**
* @Classname IdCard
* @Description 深克隆的示例2
* @Date 2021/3/19 12:33
* @Created by dell
*/
//实现Serializable接口
public class IdCard implements Serializable {
private String id;
public IdCard(String id) {
this.id = id;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Override
public String toString() {
return "IdCard{" +
"id='" + id + '\'' +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
Person
package com.zhuang.prototype.deepclone.two;
import java.io.*;
/**
* @Classname Person
* @Description 深克隆的示例2
* @Date 2021/3/19 12:33
* @Created by dell
*/
public class Person implements Serializable {
private String name;
private int age;
private IdCard idCard;
public Person(String name, int age, IdCard idCard) {
this.name = name;
this.age = age;
this.idCard = idCard;
}
public IdCard getIdCard() {
return idCard;
}
public void setIdCard(IdCard idCard) {
this.idCard = idCard;
}
@Override
public String toString() {
return "Person{" +
"personHashCode=" + this.hashCode() +
", name='" + name + '\'' +
", age=" + age +
", idCard=" + idCard +
", idCardHashCode=" + idCard.hashCode() +
'}';
}
//序列化的方式
public Person deelClone() {
//创建流对象
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
try {
//序列化
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(this);
//反序列化
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
return (Person) ois.readObject();
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
try {
ois.close();
bis.close();
oos.close();
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
PersonTest
package com.zhuang.prototype.deepclone.two;
/**
* @Classname PersonTest
* @Description 深克隆测试类
* @Date 2021/3/19 12:33
* @Created by dell
*/
public class PersonTest {
public static void main(String[] args) throws Exception {
//创建一个对象
Person person = new Person("张三", 20, new IdCard("10086"));
//克隆两个对象
Person person1 = (Person) person.deelClone();
Person person2 = (Person) person.deelClone();
System.out.println("深拷贝(第二种 实现序列化接口)");
//打印三人信息
System.out.println(person);
System.out.println(person1);
System.out.println(person2);
}
}
这种方式我们需要手动编写deepClone方法,使用Java流中的序列化与反序列化来实现深克隆,但是这种实现,需要在每一个类中都继承序列化Serializable接口,这种方式,如果你调用的是第三方类,也有可能第三方类上没有实现Serializable序列化接口,但是一般来说,大多都会实现,总的来说,这种比较推荐使用,而且效率也高
工厂模式的定义:定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类当中。这满足创建型模式中所要求的“创建与使用相分离”的特点。
按实际业务场景划分,工厂模式有 3 种不同的实现方式,分别是简单工厂模式、工厂方法模式和抽象工厂模式。
我们把被创建的对象称为“产品”,把创建产品的对象称为“工厂”。如果要创建的产品不多,只要一个工厂类就可以完成,这种模式叫“简单工厂模式”。
在简单工厂模式中创建实例的方法通常为静态(static)方法,因此简单工厂模式(Simple Factory Pattern)又叫作静态工厂方法模式(Static Factory Method Pattern)。
简单来说,简单工厂模式有一个具体的工厂类,可以生成多个不同的产品,属于创建型设计模式。简单工厂模式不在 GoF 23 种设计模式之列。
简单工厂模式每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度,违背了“开闭原则”。
优点:
缺点:
简单工厂模式的主要角色如下:
Shape
package com.zhuang.factory.simplefactory;
/**
* @Classname Shape
* @Description 产品接口类
* @Date 2021/3/18 15:40
* @Created by dell
*/
public interface Shape {
void draw();
}
Circle
package com.zhuang.factory.simplefactory;
/**
* @Classname Circle
* @Description 产品实现类
* @Date 2021/3/18 15:43
* @Created by dell
*/
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Rectangle-->绘制圆形");
}
}
Rectangle
package com.zhuang.factory.simplefactory;
/**
* @Classname Rectangle
* @Description 产品实现类
* @Date 2021/3/18 15:40
* @Created by dell
*/
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Rectangle-->绘制长方形");
}
}
Square
package com.zhuang.factory.simplefactory;
/**
* @Classname Square
* @Description 产品实现类
* @Date 2021/3/18 15:40
* @Created by dell
*/
public class Square implements Shape {
@Override
public void draw() {
System.out.println("Rectangle-->绘制方形");
}
}
ShapeFactory
package com.zhuang.factory.simplefactory;
/**
* @Classname ShapeFactory
* @Description 简单工厂类
* @Date 2021/3/18 15:43
* @Created by dell
*/
public class ShapeFactory {
public static Shape createShape(String shapeType) {
if ("Rectangle".equalsIgnoreCase(shapeType)) {
return new Rectangle();
}
if ("Circle".equalsIgnoreCase(shapeType)) {
return new Circle();
}
if ("Square".equalsIgnoreCase(shapeType)) {
return new Square();
}
return null;
}
}
ShapeFactoryTest
package com.zhuang.factory.simplefactory;
/**
* @Classname ShapeFactoryTest
* @Description 简单工厂测试类
* @Date 2021/3/18 15:47
* @Created by dell
*/
public class ShapeFactoryTest {
public static void main(String[] args) {
Shape rectangle = ShapeFactory.createShape("Rectangle");
rectangle.draw();
Shape circle = ShapeFactory.createShape("Circle");
circle.draw();
Shape square = ShapeFactory.createShape("Square");
square.draw();
}
}
Java.util.Calendar 源码使用到了简单工厂模式
抽象工厂模式(Abstract Factory)是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类,每个生成的工厂都能按照工厂模式提供对象。
使用抽象工厂模式一般要满足以下条件。
主要优点如下:
其缺点是:
Shape
package com.zhuang.factory.absfactory;
/**
* @Classname Shape
* @Description 产品接口类
* @Date 2021/3/18 15:40
* @Created by dell
*/
public interface Shape {
void draw();
}
Circle
package com.zhuang.factory.absfactory;
/**
* @Classname Circle
* @Description 产品实现类
* @Date 2021/3/18 15:43
* @Created by dell
*/
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Rectangle-->绘制圆形");
}
}
Square
package com.zhuang.factory.absfactory;
/**
* @Classname Square
* @Description 产品实现类
* @Date 2021/3/18 15:40
* @Created by dell
*/
public class Square implements Shape {
@Override
public void draw() {
System.out.println("Rectangle-->绘制方形");
}
}
Rectangle
package com.zhuang.factory.absfactory;
/**
* @Classname Rectangle
* @Description 产品实现类
* @Date 2021/3/18 15:40
* @Created by dell
*/
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Rectangle-->绘制长方形");
}
}
ShapeFactory
package com.zhuang.factory.absfactory;
/**
* @Classname ShapeFactory
* @Description 简单工厂类
* @Date 2021/3/18 15:43
* @Created by dell
*/
public class ShapeFactory extends AbstractFactory {
@Override
public Shape createShape(String shapeType) {
if ("Rectangle".equalsIgnoreCase(shapeType)) {
return new Rectangle();
}
if ("Circle".equalsIgnoreCase(shapeType)) {
return new Circle();
}
if ("Square".equalsIgnoreCase(shapeType)) {
return new Square();
}
return null;
}
@Override
public Color createColor(String colorType) {
return null;
}
}
Color
package com.zhuang.factory.absfactory;
/**
* @Classname Color
* @Description 颜色产品接口类
* @Date 2021/3/18 16:03
* @Created by dell
*/
public interface Color {
void fill();
}
Red
package com.zhuang.factory.absfactory;
/**
* @Classname Red
* @Description 颜色产品实现类
* @Date 2021/3/18 16:03
* @Created by dell
*/
public class Red implements Color {
@Override
public void fill() {
System.out.println("Red-->填充红色");
}
}
Yellow
package com.zhuang.factory.absfactory;
/**
* @Classname Yellow
* @Description 颜色产品实现类
* @Date 2021/3/18 16:04
* @Created by dell
*/
public class Yellow implements Color {
@Override
public void fill() {
System.out.println("Red-->填充黄色");
}
}
Black
package com.zhuang.factory.absfactory;
/**
* @Classname Black
* @Description 颜色产品实现类
* @Date 2021/3/18 16:05
* @Created by dell
*/
public class Black implements Color {
@Override
public void fill() {
System.out.println("Red-->填充黑色");
}
}
ColorFactory
package com.zhuang.factory.absfactory;
import com.zhuang.factory.simplefactory.Circle;
import com.zhuang.factory.simplefactory.Rectangle;
import com.zhuang.factory.simplefactory.Square;
/**
* @Classname ColorFactory
* @Description 颜色工厂类的编写
* @Date 2021/3/18 16:05
* @Created by dell
*/
public class ColorFactory extends AbstractFactory {
@Override
public Shape createShape(String shapeType) {
return null;
}
@Override
public Color createColor(String colorType) {
if ("Red".equalsIgnoreCase(colorType)) {
return new Red();
}
if ("Black".equalsIgnoreCase(colorType)) {
return new Black();
}
if ("Yellow".equalsIgnoreCase(colorType)) {
return new Yellow();
}
return null;
}
}
AbstractFactory
package com.zhuang.factory.absfactory;
/**
* @Classname AbstractFactory
* @Description 产品家族抽象类
* @Date 2021/3/18 16:06
* @Created by dell
*/
public abstract class AbstractFactory {
public abstract Shape createShape(String shapeType);
public abstract Color createColor(String colorType);
}
AbstractFactoryProducer
package com.zhuang.factory.absfactory;
/**
* @Classname AbstractFactoryProducer
* @Description 抽象类的工厂类
* @Date 2021/3/18 16:10
* @Created by dell
*/
public class AbstractFactoryProducer {
public static AbstractFactory createFactory(String choice) {
if ("Shape".equalsIgnoreCase(choice)) {
return new ShapeFactory();
}
if ("Color".equalsIgnoreCase(choice)) {
return new ColorFactory();
}
return null;
}
}
AbstractFactoryProducerTest
package com.zhuang.factory.absfactory;
/**
* @Classname AbstractFactoryProcucerTest
* @Description 抽象类的工厂类测试类
* @Date 2021/3/18 16:15
* @Created by dell
*/
public class AbstractFactoryProducerTest {
public static void main(String[] args) {
AbstractFactory shapeFactory = AbstractFactoryProducer.createFactory("Shape");
assert shapeFactory != null;
Shape rectangle = shapeFactory.createShape("Rectangle");
Shape circle = shapeFactory.createShape("Circle");
Shape square = shapeFactory.createShape("Square");
rectangle.draw();
circle.draw();
square.draw();
System.out.println("====================================");
AbstractFactory colorFactory = AbstractFactoryProducer.createFactory("Color");
Color red = colorFactory.createColor("Red");
Color yellow = colorFactory.createColor("Yellow");
Color black = colorFactory.createColor("Black");
red.fill();
yellow.fill();
black.fill();
}
}
建造者(Builder)模式的定义:指将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,这样的设计模式被称为建造者模式。它是将一个复杂的对象分解为多个简单的对象,然后一步一步构建而成。它将变与不变相分离,即产品的组成部分是不变的,但每一部分是可以灵活选择的。
该模式的主要优点如下:
其缺点如下:
House 产品角色
package com.zhuang.builder;
/**
* @Classname House
* @Description 产品实现类
* @Date 2021/3/20 11:49
* @Created by dell
*/
public class House {
private String ground;
private String wall;
private String roofed;
public House() {
}
public House(String ground, String wall, String roofed) {
this.ground = ground;
this.wall = wall;
this.roofed = roofed;
}
public String getGround() {
return ground;
}
public void setGround(String ground) {
this.ground = ground;
}
public String getWall() {
return wall;
}
public void setWall(String wall) {
this.wall = wall;
}
public String getRoofed() {
return roofed;
}
public void setRoofed(String roofed) {
this.roofed = roofed;
}
@Override
public String toString() {
return "House{" +
"ground='" + ground + '\'' +
", wall='" + wall + '\'' +
", roofed='" + roofed + '\'' +
'}';
}
}
HouseBuilder 抽象建造者
package com.zhuang.builder;
/**
* @Classname HouseBuilder
* @Description 抽象建造者
* @Date 2021/3/20 11:49
* @Created by dell
*/
public abstract class HouseBuilder {
//创建产品对象
protected House house = new House();
//生产产品流程
public abstract void buildGround();
public abstract void buildWall();
public abstract void buildRoofed();
//返回产品对象
public House getHouse() {
return house;
}
}
HighHouse 具体建造者
package com.zhuang.builder;
/**
* @Classname HighHouse
* @Description 具体建造者
* @Date 2021/3/20 11:51
* @Created by dell
*/
public class HighHouse extends HouseBuilder {
@Override
public void buildGround() {
house.setGround("100平");
System.out.println("高楼:打地基");
}
@Override
public void buildWall() {
house.setWall("50米");
System.out.println("高楼:砌墙50米");
}
@Override
public void buildRoofed() {
house.setRoofed("天窗");
System.out.println("别墅:盖天窗");
}
}
VillaHouse 具体建造者
package com.zhuang.builder;
/**
* @Classname VillaHouse
* @Description 具体建造者
* @Date 2021/3/20 11:51
* @Created by dell
*/
public class VillaHouse extends HouseBuilder {
@Override
public void buildGround() {
house.setGround("200平");
System.out.println("别墅:打地基");
}
@Override
public void buildWall() {
house.setWall("10米");
System.out.println("别墅:砌墙10米");
}
@Override
public void buildRoofed() {
house.setRoofed("天花板");
System.out.println("别墅:盖天花板");
}
}
HouseDirector 指挥者
package com.zhuang.builder;
/**
* @Classname HouseDirector
* @Description 工程指挥者
* @Date 2021/3/20 11:50
* @Created by dell
*/
public class HouseDirector {
private HouseBuilder houseBuilder;
public HouseDirector(HouseBuilder houseBuilder) {
this.houseBuilder = houseBuilder;
}
public House build() {
houseBuilder.buildGround();
houseBuilder.buildWall();
houseBuilder.buildRoofed();
return houseBuilder.getHouse();
}
}
Client
package com.zhuang.builder;
/**
* @Classname Client
* @Description 产品试用客户端
* @Date 2021/3/20 11:51
* @Created by dell
*/
public class Client {
public static void main(String[] args) {
House house1 = new HouseDirector(new VillaHouse()).build();
System.out.println(house1);
System.out.println("============================================");
House house2 = new HouseDirector(new HighHouse()).build();
System.out.println(house2);
}
}
工厂方法模式注重的是整体对象的创建方式;而建造者模式注重的是部件构建的过程,意在通过一步一步地精确构造创建出一个复杂的对象。
我们举个简单例子来说明两者的差异,如要制造一个超人,如果使用工厂方法模式,直接产生出来的就是一个力大无穷、能够飞翔、内裤外穿的超人;而如果使用建造者模式,则需要组装手、头、脚、躯干等部分,然后再把内裤外穿,于是一个超人就诞生了。
抽象工厂模式实现对产品家族的创建,一个产品家族是这样的一系列产品:具有不同分类维度的产品组合,采用抽象工厂模式则是不需要关心构建过程,只关心什么产品由什么工厂生产即可。
建造者模式则是要求按照指定的蓝图建造产品,它的主要目的是通过组装零配件而产生一个新产品。
如果将抽象工厂模式看成汽车配件生产工厂,生产一个产品族的产品,那么建造者模式就是一个汽车组装工厂,通过对部件的组装可以返回一辆完整的汽车。
适配器模式(Adapter)的定义如下:**将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。**适配器模式分为类结构型模式和对象结构型模式两种,前者类之间的耦合度比后者高。
该模式的主要优点如下:
其缺点是:
Voltage5V 目标接口
package com.zhuang.adapter.classadapter;
/**
* @Classname Voltage5V
* @Description 定义直流电
* @Date 2021/3/21 14:14
* @Created by dell
*/
public interface Voltage5V {
//定义一个标准充电器来实现
public int output5V();
}
Voltage220V
package com.zhuang.adapter.classadapter;
/**
* @Classname Voltage220V
* @Description 创建交流电
* @Date 2021/3/21 14:13
* @Created by dell
*/
public class Voltage220V {
public int output220V() {
System.out.println("voltage 输出220伏");
return 220;
}
}
VoltageAdapter
package com.zhuang.adapter.classadapter;
/**
* @Classname VoltageAdapter
* @Description 创建充电器
* @Date 2021/3/21 14:14
* @Created by dell
*/
public class VoltageAdapter extends Voltage220V implements Voltage5V {
@Override
public int output5V() {
//获取交流电220V
int output220V = output220V();
//转为5V
int output5V = output220V / 44;
System.out.println("VoltageAdapter 输出5伏");
return output5V;
}
}
Phone
package com.zhuang.adapter.classadapter;
/**
* @Classname Phone
* @Description 手机类
* @Date 2021/3/21 14:15
* @Created by dell
*/
public class Phone {
public void charging(Voltage5V voltage5V) {
if (voltage5V.output5V() == 5) {
System.out.println("电压5伏,可以充电");
} else if (voltage5V.output5V() > 5) {
System.out.println("电压过大,不可以充电");
}
}
}
Client
package com.zhuang.adapter.classadapter;
/**
* @Classname Client
* @Description 客户端类
* @Date 2021/3/21 14:15
* @Created by dell
*/
public class Client {
public static void main(String[] args) {
System.out.println("==类适配器==");
Phone phone = new Phone();
phone.charging(new VoltageAdapter());
}
}
类适配器模式注意事项和细节
Voltage5V
package com.zhuang.adapter.objectadapter;
/**
* @Classname Voltage5V
* @Description 充电5V
* @Date 2021/3/21 14:32
* @Created by dell
*/
public interface Voltage5V {
//定义一个标准充电器来实现
public int output5V();
}
Voltage220V
package com.zhuang.adapter.objectadapter;
/**
* @Classname Voltage220V
* @Description 输出220V类
* @Date 2021/3/21 14:32
* @Created by dell
*/
public class Voltage220V {
public int output220V() {
System.out.println("voltage 输出220伏");
return 220;
}
}
VoltageAdapter
package com.zhuang.adapter.objectadapter;
/**
* @Classname VoltageAdapter
* @Description 适配器类
* @Date 2021/3/21 14:33
* @Created by dell
*/
public class VoltageAdapter implements Voltage5V {
private Voltage220V voltage220V;
public VoltageAdapter(Voltage220V voltage220V) {
this.voltage220V = voltage220V;
}
@Override
public int output5V() {
//获取交流电220V
int output220V = voltage220V.output220V();
//转为5V
int output5V = output220V / 44;
System.out.println("VoltageAdapter 输出5伏");
return output5V;
}
}
Phone
package com.zhuang.adapter.objectadapter;
/**
* @Classname Phone
* @Description 手机类
* @Date 2021/3/21 14:33
* @Created by dell
*/
public class Phone {
public void charging(Voltage5V voltage5V) {
if (voltage5V.output5V() == 5) {
System.out.println("电压5伏,可以充电");
} else if (voltage5V.output5V() > 5) {
System.out.println("电压过大,不可以充电");
}
}
}
Client
package com.zhuang.adapter.objectadapter;
/**
* @Classname Client
* @Description 对象适配器测试类
* @Date 2021/3/21 14:33
* @Created by dell
*/
public class Client {
public static void main(String[] args) {
System.out.println("==对象适配器==");
Phone phone = new Phone();
phone.charging(new VoltageAdapter(new Voltage220V()));
}
}
对象适配器模式注意事项和细节
对象适配器和类适配器其实算是同一种思想,只不过实现方式不同。
根据合成复用原则,使用组合替代继承, 所以它解决了类适配器中VoltageAdapter必须继承Voltage220V的局限性问题,也不再强制要求Voltage5V必须是接口。使用成本更低,更灵活。因此,对象适配器模式是适配器模式常用的一种。
Animation
package com.zhuang.adapter.interfaceadapter;
/**
* @Classname Animation
* @Description 动画接口
* @Date 2021/3/21 14:47
* @Created by dell
*/
public interface Animation {
public void method1();
public void method2();
public void method3();
public void method4();
public void method5();
}
AnimationAdapter
package com.zhuang.adapter.interfaceadapter;
/**
* @Classname AnimationAdapter
* @Description 接口适配器类
* @Date 2021/3/21 14:48
* @Created by dell
*/
public class AnimationAdapter implements Animation {
//全部都空实现
@Override
public void method1() {
}
@Override
public void method2() {
}
@Override
public void method3() {
}
@Override
public void method4() {
}
@Override
public void method5() {
}
}
JFrameAnimation
package com.zhuang.adapter.interfaceadapter;
/**
* @Classname JFrameAnimation
* @Description 适配器子类
* @Date 2021/3/21 14:49
* @Created by dell
*/
public class JFrameAnimation extends AnimationAdapter {
@Override
public void method1() {
System.out.println("method1()被调用了...");
}
@Override
public void method2() {
System.out.println("method2()被调用了...");
}
}
Client
package com.zhuang.adapter.interfaceadapter;
/**
* @Classname Client
* @Description 客户端类
* @Date 2021/3/21 14:50
* @Created by dell
*/
public class Client {
public static void main(String[] args) {
JFrameAnimation animation = new JFrameAnimation();
animation.method1();
animation.method2();
}
}
Controller
package com.zhuang.adapter.springmvc;
/**
* @Classname Controller
* @Description springmvc的Controller源码
* @Date 2021/3/21 14:53
* @Created by dell
*/
//多种Controller实现
public interface Controller {
}
class HttpController implements Controller {
public void doHttpHandler() {
System.out.println("http...");
}
}
class SimpleController implements Controller {
public void doSimplerHandler() {
System.out.println("simple...");
}
}
class AnnotationController implements Controller {
public void doAnnotationHandler() {
System.out.println("annotation...");
}
}
DispatchServlet
package com.zhuang.adapter.springmvc;
import java.util.ArrayList;
import java.util.List;
/**
* @Classname DispatchServlet
* @Description springmvc的DispatchServlet源码
* @Date 2021/3/21 14:52
* @Created by dell
*/
public class DispatchServlet {
public static List<HandlerAdapter> handlerAdapters = new ArrayList<HandlerAdapter>();
public DispatchServlet() {
handlerAdapters.add(new AnnotationHandlerAdapter());
handlerAdapters.add(new HttpHandlerAdapter());
handlerAdapters.add(new SimpleHandlerAdapter());
}
public void doDispatch() {
// 此处模拟SpringMVC从request取handler的对象,
// 适配器可以获取到希望的Controller
HttpController controller = new HttpController();
// AnnotationController controller = new AnnotationController();
//SimpleController controller = new SimpleController();
// 得到对应适配器
HandlerAdapter adapter = getHandler(controller);
// 通过适配器执行对应的controller对应方法
adapter.handle(controller);
}
public HandlerAdapter getHandler(Controller controller) {
//遍历:根据得到的controller(handler), 返回对应适配器
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(controller)) {
return adapter;
}
}
return null;
}
public static void main(String[] args) {
new DispatchServlet().doDispatch(); // http...
}
}
HandlerAdapter
package com.zhuang.adapter.springmvc;
/**
* @Classname HandlerAdapter
* @Description springmvc的HandlerAdapter源码
* @Date 2021/3/21 14:53
* @Created by dell
*/
///定义一个Adapter接口
public interface HandlerAdapter {
public boolean supports(Object handler);
public void handle(Object handler);
}
// 多种适配器类
class SimpleHandlerAdapter implements HandlerAdapter {
@Override
public void handle(Object handler) {
((SimpleController) handler).doSimplerHandler();
}
@Override
public boolean supports(Object handler) {
return (handler instanceof SimpleController);
}
}
class HttpHandlerAdapter implements HandlerAdapter {
@Override
public void handle(Object handler) {
((HttpController) handler).doHttpHandler();
}
@Override
public boolean supports(Object handler) {
return (handler instanceof HttpController);
}
}
class AnnotationHandlerAdapter implements HandlerAdapter {
@Override
public void handle(Object handler) {
((AnnotationController) handler).doAnnotationHandler();
}
@Override
public boolean supports(Object handler) {
return (handler instanceof AnnotationController);
}
}
桥接(Bridge)模式的定义如下:**将抽象与实现分离,使它们可以独立变化。**它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。
通过上面的讲解,我们能很好的感觉到桥接模式遵循了里氏替换原则和依赖倒置原则,最终实现了开闭原则,对修改关闭,对扩展开放。
桥接(Bridge)模式的优点是:
缺点是:
由于聚合关系建立在抽象层,要求开发者针对抽象化进行设计与编程,能正确地识别出系统中两个独立变化的维度,这增加了系统的理解与设计难度。
<img alt="">
Brand 抽象化角色
package com.zhuang.bridge;
/**
* @Classname Brand
* @Description 品牌类
* @Date 2021/3/22 9:31
* @Created by dell
*/
public interface Brand {
void open();
void call();
void close();
}
Vivo 拓展抽象化角色
package com.zhuang.bridge;
/**
* @Classname Vivo
* @Description 手机品牌 实现品牌接口
* @Date 2021/3/22 9:30
* @Created by dell
*/
public class Vivo implements Brand {
@Override
public void open() {
System.out.println("Vivo手机开机");
}
@Override
public void call() {
System.out.println("Vivo手机打电话");
}
@Override
public void close() {
System.out.println("Vivo手机关机");
}
}
XiaoMi 拓展抽象化角色
package com.zhuang.bridge;
/**
* @Classname XiaoMi
* @Description 手机品牌 实现品牌接口
* @Date 2021/3/22 9:30
* @Created by dell
*/
public class XiaoMi implements Brand {
@Override
public void open() {
System.out.println("XiaoMi手机开机");
}
@Override
public void call() {
System.out.println("XiaoMi手机打电话");
}
@Override
public void close() {
System.out.println("XiaoMi手机关机");
}
}
Phone
package com.zhuang.bridge;
/**
* @Classname Phone
* @Description 手机类 抽象类
* @Date 2021/3/22 9:30
* @Created by dell
*/
public abstract class Phone {
//组合品牌
private Brand brand;
//构造器
public Phone(Brand brand) {
super();
this.brand = brand;
}
protected void open() {
this.brand.open();
}
protected void close() {
this.brand.close();
}
protected void call() {
this.brand.call();
}
}
FoldedPhone
package com.zhuang.bridge;
/**
* @Classname FoldedPhone
* @Description 折叠手机类
* @Date 2021/3/22 9:31
* @Created by dell
*/
public class FoldedPhone extends Phone {
//构造器
public FoldedPhone(Brand brand) {
super(brand);
}
@Override
public void open() {
super.open();
System.out.println("折叠手机样式");
}
@Override
public void call() {
super.call();
System.out.println("折叠手机样式");
}
@Override
public void close() {
super.close();
System.out.println("折叠手机样式");
}
}
UpRightPhone
package com.zhuang.bridge;
/**
* @Classname UpRightPhone
* @Description 直立手机类
* @Date 2021/3/22 9:33
* @Created by dell
*/
public class UpRightPhone extends Phone {
//构造器
public UpRightPhone(Brand brand) {
super(brand);
}
@Override
public void open() {
super.open();
System.out.println("直立手机样式");
}
@Override
public void call() {
super.call();
System.out.println("直立手机样式");
}
@Override
public void close() {
super.close();
System.out.println("直立手机样式");
}
}
Client
package com.zhuang.bridge;
/**
* @Classname Client
* @Description 客户端类
* @Date 2021/3/22 9:30
* @Created by dell
*/
public class Client {
public static void main(String[] args) {
Phone phone1 = new FoldedPhone(new XiaoMi());
phone1.open();
phone1.call();
phone1.close();
System.out.println("==============================");
Phone phone2 = new UpRightPhone(new Vivo());
phone2.open();
phone2.call();
phone2.close();
}
}
当一个类内部具备两种或多种变化维度时,使用桥接模式可以解耦这些变化的维度,使高层代码架构稳定。
装饰器(Decorator)模式的定义:指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,它属于对象结构型模式。
装饰器模式的主要优点有:
其主要缺点是:
通常情况下,扩展一个类的功能会使用继承方式来实现。但继承具有静态特征,耦合度高,并且随着扩展功能的增多,子类会很膨胀。如果使用组合关系来创建一个包装对象(即装饰对象)来包裹真实对象,并在保持真实对象的类结构不变的前提下,为其提供额外的功能,这就是装饰器模式的目标
关系类图
FastFood 抽象构件角色
package com.zhuang.decorator;
/**
* @Classname FastFood
* @Description 快餐类 接口
* @Date 2021/3/23 21:54
* @Created by dell
*/
public abstract class FastFood {
private float price;
private String desc; //描述
public FastFood() {
}
public FastFood(float price, String desc) {
this.price = price;
this.desc = desc;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
//抽象方法 获取价格
public abstract float cost();
}
FiredRice 具体构建角色
package com.zhuang.decorator;
/**
* @Classname FiredRice
* @Description 炒饭类 继承快餐类
* @Date 2021/3/23 21:54
* @Created by dell
*/
public class FiredRice extends FastFood {
public FiredRice() {
super(10, "炒饭");
}
@Override
public float cost() {
return getPrice();
}
}
FiredNoodles 具体构建角色
package com.zhuang.decorator;
/**
* @Classname FiredNoodles
* @Description 炒面类 继承 快餐类
* @Date 2021/3/23 21:54
* @Created by dell
*/
public class FiredNoodles extends FastFood {
public FiredNoodles() {
super(15, "炒面");
}
@Override
public float cost() {
return getPrice();
}
}
Garnish 抽象装饰角色
package com.zhuang.decorator;
/**
* @Classname Garnish
* @Description 抽象配料类 继承快餐类
* @Date 2021/3/23 21:58
* @Created by dell
*/
public abstract class Garnish extends FastFood{
private FastFood fastFood;
public FastFood getFastFood() {
return fastFood;
}
public void setFastFood(FastFood fastFood) {
this.fastFood = fastFood;
}
public Garnish(FastFood fastFood,float price,String desc){
super(price,desc);
this.fastFood = fastFood;
}
}
Egg 具体装饰角色
package com.zhuang.decorator;
/**
* @Classname Egg
* @Description 鸡蛋配料类 继承配料类
* @Date 2021/3/23 21:55
* @Created by dell
*/
public class Egg extends Garnish {
public Egg(FastFood fastFood) {
//鸡蛋1元
super(fastFood, 1, "鸡蛋");
}
@Override
public float cost() {
return getPrice() + getFastFood().getPrice();
}
@Override
public String getDesc() {
return super.getDesc() + getFastFood().getDesc();
}
}
Bacon 具体装饰角色
package com.zhuang.decorator;
/**
* @Classname Bacon
* @Description 培根类 继承配料类
* @Date 2021/3/23 22:03
* @Created by dell
*/
public class Bacon extends Garnish {
public Bacon(FastFood fastFood) {
//培根2元
super(fastFood, 2, "培根");
}
@Override
public float cost() {
return getPrice() + getFastFood().getPrice();
}
@Override
public String getDesc() {
return super.getDesc() + getFastFood().getDesc();
}
}
Client
package com.zhuang.decorator;
/**
* @Classname Client
* @Description 装饰器模式测试类
* @Date 2021/3/23 21:53
* @Created by dell
*/
public class Client {
public static void main(String[] args) {
//点一份炒饭
FastFood rice = new FiredRice();
//价格
System.out.println(rice.getDesc() + "-->" + rice.cost() + "元");
System.out.println("=============================");
//点一份加鸡蛋的炒饭
FastFood eggRice = new FiredRice();
//加鸡蛋
eggRice = new Egg(eggRice);
System.out.println(eggRice.getDesc() + "-->" + eggRice.cost() + "元");
System.out.println("=============================");
//点一份加培根的炒面
FastFood baconNoodles = new FiredNoodles();
//加培根
baconNoodles = new Bacon(baconNoodles);
System.out.println(baconNoodles.getDesc() + "-->" + baconNoodles.cost() + "元");
}
}
当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时。
不能采用继承的情况主要有两类:
在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
当对象的功能要求可以动态地添加,也可以再动态地撤销时。
IO流中的包装类使用到了装饰者模式。BufferedInputStream,BufferedOutputStream,BufferedReader,BufferedWriter。
我们以BufferedWriter举例来说明,先看看如何使用BufferedWriter
public class Demo {
public static void main(String[] args) throws Exception{
//创建BufferedWriter对象
//创建FileWriter对象
FileWriter fw = new FileWriter("C:\\Users\\dell\\Desktop\\a.txt");
BufferedWriter bw = new BufferedWriter(fw);
//写数据
bw.write("hello Buffered");
bw.close();
}
}
结构
BufferedWriter使用装饰者模式对Writer子实现类进行了增强,添加了缓冲区,提高了写数据的效率。
静态代理和装饰者模式的区别:
外观(Facade)模式又叫作门面模式,**是一种通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式。**该模式对外有一个统一接口,外部应用程序不用关心内部子系统的具体细节,这样会大大降低应用程序的复杂度,提高了程序的可维护性。
外观(Facade)模式是“迪米特法则”的典型应用,它有以下主要优点。
外观(Facade)模式的主要缺点如下。
外观(Facade)模式包含以下主要角色。
关系类图
AirCondition
package com.zhuang.facade;
/**
* @Classname AirCondition
* @Description 空调类
* @Date 2021/3/24 19:23
* @Created by dell
*/
public class AirCondition {
public void on() {
System.out.println("空调打开...");
}
public void off() {
System.out.println("空调关闭...");
}
}
Light
package com.zhuang.facade;
/**
* @Classname Light
* @Description 电灯类
* @Date 2021/3/24 19:23
* @Created by dell
*/
public class Light {
public void on() {
System.out.println("电灯打开...");
}
public void off() {
System.out.println("电灯关闭...");
}
}
TV
package com.zhuang.facade;
/**
* @Classname TV
* @Description 电视类
* @Date 2021/3/24 19:23
* @Created by dell
*/
public class TV {
public void on() {
System.out.println("电视打开...");
}
public void off() {
System.out.println("电视关闭...");
}
}
SmartAppliancesFacade
package com.zhuang.facade;
/**
* @Classname SmartAppliancesFacade
* @Description 智能音箱类 外观类
* @Date 2021/3/24 19:24
* @Created by dell
*/
public class SmartAppliancesFacade {
private Light light;
private TV tv;
private AirCondition airCondition;
public SmartAppliancesFacade() {
light = new Light();
tv = new TV();
airCondition = new AirCondition();
}
//私有打开方法 外界访问不了
//一键打开
private void on() {
light.on();
tv.on();
airCondition.on();
}
//私有关闭方法 外界访问不了
//一键关闭
private void off() {
light.off();
tv.off();
airCondition.off();
}
//判断方法
public void say(String message) {
if (message.contains("打开")) {
on();
} else if (message.contains("关闭")) {
off();
} else {
System.out.println("你说的指令我听不懂!!!");
}
}
}
Client
package com.zhuang.facade;
/**
* @Classname Client
* @Description 外观模式测试类
* @Date 2021/3/24 19:24
* @Created by dell
*/
public class Client {
public static void main(String[] args) {
SmartAppliancesFacade smartAppliancesFacade = new SmartAppliancesFacade();
smartAppliancesFacade.say("打开家电");
System.out.println("=================");
smartAppliancesFacade.say("关闭家电");
}
}
使用tomcat作为web容器时,接收浏览器发送过来的请求,tomcat会将请求信息封装成ServletRequest对象,如下图①处对象。但是大家想想ServletRequest是一个接口,它还有一个子接口HttpServletRequest,而我们知道该request对象肯定是一个HttpServletRequest对象的子实现类对象,到底是哪个类的对象呢?可以通过输出request对象,我们就会发现是一个名为RequestFacade的类的对象。
RequestFacade类就使用了外观模式
为什么在此处使用外观模式呢?
定义 RequestFacade 类,分别实现 ServletRequest ,同时定义私有成员变量 Request ,并且方法的实现调用 Request 的实现。然后,将 RequestFacade上转为 ServletRequest 传给 servlet 的 service 方法,这样即使在 servlet 中被下转为 RequestFacade ,也不能访问私有成员变量对象中的方法。既用了 Request ,又能防止其中方法被不合理的访问。
享元(Flyweight)模式的定义:**运用共享技术来有效地支持大量细粒度对象的复用。**它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率。
享元模式的主要优点是:相同对象只要保存一份,这降低了系统中对象的数量,从而降低了系统中细粒度对象给内存带来的压力。
其主要缺点是:
享元(Flyweight )模式中存在以下两种状态:
享元模式的主要有以下角色:
关系类图
IBox 定义不同的形状
package com.zhuang.flyweight;
/**
* @Classname IBox
* @Description I图形类(具体享元角色)
* @Date 2021/3/25 15:33
* @Created by dell
*/
public class IBox extends AbstractBox {
@Override
public String getShape() {
return "I";
}
}
LBox 定义不同的形状
package com.zhuang.flyweight;
/**
* @Classname IBox
* @Description L图形类(具体享元角色)
* @Date 2021/3/25 15:33
* @Created by dell
*/
public class LBox extends AbstractBox {
@Override
public String getShape() {
return "L";
}
}
OBox 定义不同的形状
package com.zhuang.flyweight;
/**
* @Classname IBox
* @Description O图形类(具体享元角色)
* @Date 2021/3/25 15:33
* @Created by dell
*/
public class OBox extends AbstractBox {
@Override
public String getShape() {
return "O";
}
}
BoxFactory 提供了一个工厂类(BoxFactory),用来管理享元对象(也就是AbstractBox子类对象),该工厂类对象只需要一个,所以可以使用单例模式。并给工厂类提供一个获取形状的方法
package com.zhuang.flyweight;
import java.util.HashMap;
/**
* @Classname BoxFactory
* @Description 工厂类 将类设计为单例模式
* @Date 2021/3/25 15:33
* @Created by dell
*/
public class BoxFactory {
private HashMap<String, AbstractBox> map;
//在构造方法中初始化
private BoxFactory() {
map = new HashMap<String, AbstractBox>();
IBox iBox = new IBox();
LBox lBox = new LBox();
OBox oBox = new OBox();
map.put("I", iBox);
map.put("L", lBox);
map.put("O", oBox);
}
//声明一个方法获取工厂
public static BoxFactory getInstance() {
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder {
private static final BoxFactory INSTANCE = new BoxFactory();
}
//根据图形名称获取图形对象
public AbstractBox getShape(String name) {
return map.get(name);
}
}
AbstractBox 对这些形状向上抽取出AbstractBox,用来定义共性的属性和行为
package com.zhuang.flyweight;
/**
* @Classname AbstractBox
* @Description 抽象享元角色 抽象类
* @Date 2021/3/25 15:32
* @Created by dell
*/
public abstract class AbstractBox {
//获取图形的方法
public abstract String getShape();
//显示图形及颜色
public void display(String color) {
System.out.println("方块形状:" + this.getShape() + "颜色:" + color);
}
}
当系统中多处需要同一组信息时,可以把这些信息封装到一个对象中,然后对该对象进行缓存,这样,一个对象就可以提供给多出需要使用的地方,避免大量同一对象的多次创建,降低大量内存空间的消耗。
享元模式其实是工厂方法模式的一个改进机制,享元模式同样要求创建一个或一组对象,并且就是通过工厂方法模式生成对象的,只不过享元模式为工厂方法模式增加了缓存这一功能。
前面分析了享元模式的结构与特点,下面分析它适用的应用场景。享元模式是通过减少内存中对象的数量来节省内存空间的,所以以下几种情形适合采用享元模式。
Integer类使用了享元模式。我们先看下面的例子:
public class Demo {
public static void main(String[] args) {
Integer i1 = 127;
Integer i2 = 127;
System.out.println("i1和i2对象是否是同一个对象?" + (i1 == i2));
Integer i3 = 128;
Integer i4 = 128;
System.out.println("i3和i4对象是否是同一个对象?" + (i3 == i4));
}
}
结果是 true false
为什么第一个输出语句输出的是true,第二个输出语句输出的是false?通过反编译软件进行反编译,代码如下:
public class Demo {
public static void main(String[] args) {
Integer i1 = Integer.valueOf((int)127);
Integer i2 Integer.valueOf((int)127);
System.out.println((String)new StringBuilder().append((String)"i1\u548ci2\u5bf9\u8c61\u662f\u5426\u662f\u540c\u4e00\u4e2a\u5bf9\u8c61\uff1f").append((boolean)(i1 == i2)).toString());
Integer i3 = Integer.valueOf((int)128);
Integer i4 = Integer.valueOf((int)128);
System.out.println((String)new StringBuilder().append((String)"i3\u548ci4\u5bf9\u8c61\u662f\u5426\u662f\u540c\u4e00\u4e2a\u5bf9\u8c61\uff1f").append((boolean)(i3 == i4)).toString());
}
}
上面代码可以看到,直接给Integer类型的变量赋值基本数据类型数据的操作底层使用的是 valueOf()
,所以只需要看该方法即可
public final class Integer extends Number implements Comparable<Integer> {
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
}
可以看到 Integer
默认先创建并缓存 -128 ~ 127
之间数的 Integer
对象,当调用 valueOf
时如果参数在 -128 ~ 127
之间则计算下标并从缓存中返回,否则创建一个新的 Integer
对象。
模板方法(Template Method)模式的定义如下:**定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。**它是一种类行为型模式。
该模式的主要优点如下。
该模式的主要缺点如下。
模板方法(Template Method)模式包含以下主要角色:
抽象类(Abstract Class):负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成。
模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法。
基本方法:是实现算法各个步骤的方法,是模板方法的组成部分。基本方法又可以分为三种:
抽象方法(Abstract Method) :一个抽象方法由抽象类声明、由其具体子类实现。
具体方法(Concrete Method) :一个具体方法由一个抽象类或具体类声明并实现,其子类可以进行覆盖也可以直接继承。
钩子方法(Hook Method) :在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种。
一般钩子方法是用于判断的逻辑方法,这类方法名一般为isXxx,返回值类型为boolean类型。
具体子类(Concrete Class):实现抽象类中所定义的抽象方法和钩子方法,它们是一个顶级逻辑的组成步骤。
AbstractClass
package com.zhuang.template;
/**
* @Classname AbstractClass
* @Description 抽象类
* @Date 2021/3/26 20:06
* @Created by dell
*/
public abstract class AbstractClass {
public final void work() {
//起床
this.wake();
//刷牙
this.brush();
//吃早饭
this.breakfast();
//交通工具
this.transport();
//睡觉
this.sleep();
}
//步骤一样 直接实现
public void wake() {
System.out.println("起床...");
}
//步骤一样 直接实现
public void brush() {
System.out.println("刷牙...");
}
// 步骤不一样 (一个是吃面包 一个是喝牛奶)
public abstract void breakfast();
// 步骤不一样 (一个是开车 一个是坐地铁)
public abstract void transport();
// 步骤一样 直接实现
public void sleep() {
System.out.println("睡觉...");
}
}
ConcreteClass_BreakFast
package com.zhuang.template;
/**
* @Classname ConcreteClass_BreakFast
* @Description 具体类 早饭类 继承
* @Date 2021/3/26 20:13
* @Created by dell
*/
public class ConcreteClass_BreakFast extends AbstractClass {
@Override
public void breakfast() {
System.out.println("吃面包...");
}
@Override
public void transport() {
System.out.println("坐公交...");
}
}
ConcreteClass_Transport
package com.zhuang.template;
/**
* @Classname ConcreteClass_Transport
* @Description 交通工具类 继承
* @Date 2021/3/26 20:14
* @Created by dell
*/
public class ConcreteClass_Transport extends AbstractClass {
@Override
public void breakfast() {
System.out.println("喝牛奶...");
}
@Override
public void transport() {
System.out.println("乘地铁...");
}
}
Client
package com.zhuang.template;
/**
* @Classname Client
* @Description 模板方法模式 测试类
* @Date 2021/3/26 20:16
* @Created by dell
*/
public class Client {
public static void main(String[] args) {
//吃面包 坐公交
System.out.println("周一");
AbstractClass breakFast = new ConcreteClass_BreakFast();
breakFast.work();
System.out.println("========================");
System.out.println("周五");
AbstractClass transport = new ConcreteClass_Transport();
transport.work();
}
}
钩子方法
AbstractClass
package com.zhuang.template.hook_method;
/**
* @Classname AbstractClass
* @Description 抽象类
* @Date 2021/3/26 20:06
* @Created by dell
*/
public abstract class AbstractClass {
public final void work() {
//起床
this.wake();
//刷牙
this.brush();
//吃早饭
this.breakfast();
//交通工具
if (isSunday()) {
this.transport();
}
//睡觉
this.sleep();
}
//步骤一样 直接实现
public void wake() {
System.out.println("起床...");
}
//步骤一样 直接实现
public void brush() {
System.out.println("刷牙...");
}
// 步骤不一样 (一个是吃面包 一个是喝牛奶)
public abstract void breakfast();
// 步骤不一样 (一个是开车 一个是坐地铁)
public abstract void transport();
// 步骤一样 直接实现
public void sleep() {
System.out.println("睡觉...");
}
//钩子方法 是否为周末 周末不用交通工具
boolean isSunday() {
return false;
}
}
ConcreteClass_BreakFast
package com.zhuang.template.hook_method;
/**
* @Classname ConcreteClass_BreakFast
* @Description 具体类 早饭类 继承
* @Date 2021/3/26 20:13
* @Created by dell
*/
public class ConcreteClass_BreakFast extends AbstractClass {
@Override
public void breakfast() {
System.out.println("吃面包...");
}
@Override
public void transport() {
System.out.println("坐公交...");
}
}
ConcreteClass_Transport
package com.zhuang.template.hook_method;
/**
* @Classname ConcreteClass_Transport
* @Description 交通工具类 继承
* @Date 2021/3/26 20:14
* @Created by dell
*/
public class ConcreteClass_Transport extends AbstractClass {
@Override
public void breakfast() {
System.out.println("喝牛奶...");
}
@Override
public void transport() {
System.out.println("乘地铁...");
}
}
ConcreteClass_Sunday
package com.zhuang.template.hook_method;
/**
* @Classname ConcreteClass_Sunday
* @Description 周末 不用上班 空实现交通方法
* @Date 2021/3/26 20:28
* @Created by dell
*/
public class ConcreteClass_Sunday extends AbstractClass{
@Override
public void breakfast() {
System.out.println("吃面包或者喝牛奶...");
}
@Override
public void transport() {
//空实现
}
@Override
boolean isSunday() {
System.out.println("今天周末,休息...");
return true;
}
}
Client
package com.zhuang.template.hook_method;
/**
* @Classname Client
* @Description 模板方法 测试钩子方法
* @Date 2021/3/26 20:26
* @Created by dell
*/
public class Client {
public static void main(String[] args) {
AbstractClass sunday = new ConcreteClass_Sunday();
sunday.work();
}
}
InputStream类就使用了模板方法模式。在InputStream类中定义了多个 read()
方法,如下:
public abstract class InputStream implements Closeable {
//抽象方法,要求子类必须重写
public abstract int read() throws IOException;
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
public int read(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
int c = read(); //调用了无参的read方法,该方法是每次读取一个字节数据
if (c == -1) {
return -1;
}
b[off] = (byte)c;
int i = 1;
try {
for (; i < len ; i++) {
c = read();
if (c == -1) {
break;
}
b[off + i] = (byte)c;
}
} catch (IOException ee) {
}
return i;
}
}
从上面代码可以看到,无参的 read()
方法是抽象方法,要求子类必须实现。而 read(byte b[])
方法调用了 read(byte b[], int off, int len)
方法,所以在此处重点看的方法是带三个参数的方法。
在该方法中第18行、27行,可以看到调用了无参的抽象的 read()
方法。
总结如下: 在InputStream父类中已经定义好了读取一个字节数组数据的方法是每次读取一个字节,并将其存储到数组的第一个索引位置,读取len个字节数据。具体如何读取一个字节数据呢?由子类实现。
组合(Composite Pattern)模式的定义:有时又叫作整体-部分(Part-Whole)模式,它是一种将对象组合成树状的层次结构的模式,用来表示“整体-部分”的关系,使用户对单个对象和组合对象具有一致的访问性,属于结构型设计模式。
组合模式一般用来描述整体与部分的关系,它将对象组织到树形结构中,顶层的节点被称为根节点,根节点下面可以包含树枝节点和叶子节点,树枝节点下面又可以包含树枝节点和叶子节点,树形结构图如下
由上图可以看出,其实根节点和树枝节点本质上属于同一种数据类型,可以作为容器使用;而叶子节点与树枝节点在语义上不属于用一种类型。但是在组合模式中,会把树枝节点和叶子节点看作属于同一种数据类型(用统一接口定义),让它们具备一致行为。
这样,在组合模式中,整个树形结构中的对象都属于同一种类型,带来的好处就是用户不需要辨别是树枝节点还是叶子节点,可以直接进行操作,给用户的使用带来极大的便利。
组合模式的主要优点有:
其主要缺点是:
如下图,我们在访问别的一些管理系统时,经常可以看到类似的菜单。一个菜单可以包含菜单项(菜单项是指不再包含其他内容的菜单条目),也可以包含带有其他菜单项的菜单,因此使用组合模式描述菜单就很恰当,我们的需求是针对一个菜单,打印出其包含的所有菜单以及菜单项的名称。
MenuComponent MenuComponent定义为抽象类,因为有一些共有的属性和行为要在该类中实现,Menu和MenuItem类就可以只覆盖自己感兴趣的方法,而不用搭理不需要或者不感兴趣的方法,举例来说,Menu类可以包含子菜单,因此需要覆盖add()、remove()、getChild()方法,但是MenuItem就不应该有这些方法。这里给出的默认实现是抛出异常,你也可以根据自己的需要改写默认实现。
package com.zhuang.combination;
/**
* @Classname MenuComponent
* @Description 菜单组件 不管菜单还是菜单项,都应该继承该类 抽象类
* @Date 2021/3/24 17:02
* @Created by dell
*/
public abstract class MenuComponent {
protected String name;
protected int level;
//添加菜单
public void add(MenuComponent menuComponent) {
throw new UnsupportedOperationException();
}
//移除菜单
public void remove(MenuComponent menuComponent) {
throw new UnsupportedOperationException();
}
//获取指定的子菜单
public MenuComponent getChild(int i) {
throw new UnsupportedOperationException();
}
//获取菜单名称
public String getName() {
return name;
}
//打印方法
public void print() {
throw new UnsupportedOperationException();
}
}
Menu类已经实现了除了getName方法的其他所有方法,因为Menu类具有添加菜单,移除菜单和获取子菜单的功能
package com.zhuang.combination;
import java.util.ArrayList;
import java.util.List;
/**
* @Classname Menu
* @Description 菜单类 继承菜单组件
* @Date 2021/3/24 17:05
* @Created by dell
*/
public class Menu extends MenuComponent {
private List<MenuComponent> menuComponentList;
public Menu(String name, int level) {
this.name = name;
this.level = level;
menuComponentList = new ArrayList<MenuComponent>();
}
@Override
public void add(MenuComponent menuComponent) {
menuComponentList.add(menuComponent);
}
@Override
public void remove(MenuComponent menuComponent) {
menuComponentList.remove(menuComponent);
}
@Override
public MenuComponent getChild(int i) {
return menuComponentList.get(i);
}
@Override
public void print() {
for (int i = 0; i < level; i++) {
System.out.print("--");
}
System.out.println(name);
for (MenuComponent menuComponent : menuComponentList) {
menuComponent.print();
}
}
}
MenuItem是菜单项,不能再有子菜单,所以添加菜单,移除菜单和获取子菜单的功能并不能实现。
package com.zhuang.combination;
/**
* @Classname MenuItem
* @Description 菜单选项 继承菜单组件
* @Date 2021/3/24 17:10
* @Created by dell
*/
public class MenuItem extends MenuComponent {
public MenuItem(String name, int level) {
this.name = name;
this.level = level;
}
@Override
public void print() {
for (int i = 0; i < level; i++) {
System.out.print("--");
}
System.out.println(name);
}
}
在使用组合模式时,根据抽象构件类的定义形式,我们可将组合模式分为透明组合模式和安全组合模式两种形式。
透明组合模式
透明组合模式中,抽象根节点角色中声明了所有用于管理成员对象的方法,比如在示例中 MenuComponent
声明了 add
、remove
、getChild
方法,这样做的好处是确保所有的构件类都有相同的接口。透明组合模式也是组合模式的标准形式。
透明组合模式的缺点是不够安全,因为叶子对象和容器对象在本质上是有区别的,叶子对象不可能有下一个层次的对象,即不可能包含成员对象,因此为其提供 add()、remove() 等方法是没有意义的,这在编译阶段不会出错,但在运行阶段如果调用这些方法可能会出错(如果没有提供相应的错误处理代码)
安全组合模式
在安全组合模式中,在抽象构件角色中没有声明任何用于管理成员对象的方法,而是在树枝节点 Menu
类中声明并实现这些方法。安全组合模式的缺点是不够透明,因为叶子构件和容器构件具有不同的方法,且容器构件中那些用于管理成员对象的方法没有在抽象构件类中定义,因此客户端不能完全针对抽象编程,必须有区别地对待叶子构件和容器构件。
代理模式的定义:**由于某些原因需要给某对象提供一个代理以控制对该对象的访问。**这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。
代理模式的主要优点有:
其主要缺点是:
根据代理的创建时期,代理模式分为静态代理和动态代理。
关系类图
SellTickets
package com.zhuang.proxy.static_proxy;
/**
* @Classname SellTickets
* @Description 卖票接口
* @Date 2021/3/26 8:01
* @Created by dell
*/
public interface SellTickets {
void sell();
}
Transition
package com.zhuang.proxy.static_proxy;
/**
* @Classname Transition
* @Description 火车站,具有卖票功能,实现接口
* @Date 2021/3/26 8:01
* @Created by dell
*/
public class Transition implements SellTickets {
@Override
public void sell() {
System.out.println("火车站卖票");
}
}
ProxyPoint
package com.zhuang.proxy.static_proxy;
/**
* @Classname ProxyPoint
* @Description 代售点 实现接口
* @Date 2021/3/26 8:02
* @Created by dell
*/
public class ProxyPoint implements SellTickets {
private Transition transition = new Transition();
@Override
public void sell() {
System.out.println("代售点收取服务费");
transition.sell();
}
}
Client
package com.zhuang.proxy.static_proxy;
/**
* @Classname Client
* @Description 静态代理客户端测试类
* @Date 2021/3/26 8:02
* @Created by dell
*/
public class Client {
public static void main(String[] args) {
ProxyPoint proxyPoint = new ProxyPoint();
proxyPoint.sell();
}
}
从上面代码中可以看出测试类直接访问的是ProxyPoint类对象,也就是说ProxyPoint作为访问对象和目标对象的中介。同时也对sell方法进行了增强
使用动态代理实现上面案例,先说说JDK提供的动态代理。Java中提供了一个动态代理类Proxy,Proxy并不是我们上述所说的代理对象的类,而是提供了一个创建代理对象的静态方法(newProxyInstance方法)来获取代理对象。
SellTickets
package com.zhuang.proxy.jdk_proxy;
/**
* @Classname SellTickets
* @Description 卖票接口
* @Date 2021/3/26 8:01
* @Created by dell
*/
public interface SellTickets {
void sell();
}
Transition
package com.zhuang.proxy.jdk_proxy;
/**
* @Classname Transition
* @Description 火车站,具有卖票功能,实现接口
* @Date 2021/3/26 8:01
* @Created by dell
*/
public class Transition implements SellTickets {
@Override
public void sell() {
System.out.println("火车站卖票");
}
}
ProxyFactory
package com.zhuang.proxy.jdk_proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @Classname ProxyFactory
* @Description 代理工厂 用来创建代理对象
* @Date 2021/3/26 8:11
* @Created by dell
*/
public class ProxyFactory {
private Transition transition = new Transition();
public SellTickets getProxyObject() {
//使用Proxy获取代理对象
/* newProxyInstance
ClassLoader loader, 类加载器
Class<?>[] interfaces, 接口
InvocationHandler h 方法
invoke 方法参数说明
proxy 代理对象
method 对应于在代理对象调用的接口方法的Method实例
args 代理对象调用接口方法时传递的实际参数
*/
SellTickets sellTickets = (SellTickets) Proxy.newProxyInstance(transition.getClass().getClassLoader(),
transition.getClass().getInterfaces()
, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代售点收取服务费(JDK动态代理方式)");
//执行真实对象
Object result = method.invoke(transition, args);
return result;
}
});
return sellTickets;
}
}
Client
package com.zhuang.proxy.jdk_proxy;
/**
* @Classname Client
* @Description JDK动态代理 测试类
* @Date 2021/3/26 8:20
* @Created by dell
*/
public class Client {
public static void main(String[] args) {
//获取代理对象
ProxyFactory factory = new ProxyFactory();
SellTickets proxyObject = factory.getProxyObject();
proxyObject.sell();
}
}
使用了动态代理,我们思考下面问题:
ProxyFactory是代理类吗?
ProxyFactory不是代理模式中所说的代理类,而代理类是程序在运行过程中动态的在内存中生成的类。通过阿里巴巴开源的 Java 诊断工具(Arthas【阿尔萨斯】)查看代理类的结构:
package com.sun.proxy;
import com.itheima.proxy.dynamic.jdk.SellTickets;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements SellTickets {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public $Proxy0(InvocationHandler invocationHandler) {
super(invocationHandler);
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("com.itheima.proxy.dynamic.jdk.SellTickets").getMethod("sell", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
}
catch (NoSuchMethodException noSuchMethodException) {
throw new NoSuchMethodError(noSuchMethodException.getMessage());
}
catch (ClassNotFoundException classNotFoundException) {
throw new NoClassDefFoundError(classNotFoundException.getMessage());
}
}
public final boolean equals(Object object) {
try {
return (Boolean)this.h.invoke(this, m1, new Object[]{object});
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final String toString() {
try {
return (String)this.h.invoke(this, m2, null);
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final int hashCode() {
try {
return (Integer)this.h.invoke(this, m0, null);
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final void sell() {
try {
this.h.invoke(this, m3, null);
return;
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
}
从上面的类中,我们可以看到以下几个信息:
动态代理的执行流程是什么样?
下面是摘取的重点代码:
//程序运行过程中动态生成的代理类
public final class $Proxy0 extends Proxy implements SellTickets {
private static Method m3;
public $Proxy0(InvocationHandler invocationHandler) {
super(invocationHandler);
}
static {
m3 = Class.forName("com.itheima.proxy.dynamic.jdk.SellTickets").getMethod("sell", new Class[0]);
}
public final void sell() {
this.h.invoke(this, m3, null);
}
}
//Java提供的动态代理相关类
public class Proxy implements java.io.Serializable {
protected InvocationHandler h;
protected Proxy(InvocationHandler h) {
this.h = h;
}
}
//代理工厂类
public class ProxyFactory {
private TrainStation station = new TrainStation();
public SellTickets getProxyObject() {
SellTickets sellTickets = (SellTickets) Proxy.newProxyInstance(station.getClass().getClassLoader(),
station.getClass().getInterfaces(),
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理点收取一些服务费用(JDK动态代理方式)");
Object result = method.invoke(station, args);
return result;
}
});
return sellTickets;
}
}
//测试访问类
public class Client {
public static void main(String[] args) {
//获取代理对象
ProxyFactory factory = new ProxyFactory();
SellTickets proxyObject = factory.getProxyObject();
proxyObject.sell();
}
}
执行流程如下:
TrainStation
package com.zhuang.proxy.cglib_proxy;
/**
* @Classname TrainStation
* @Description 火车站类
* @Date 2021/3/26 8:31
* @Created by dell
*/
public class TrainStation {
public void sell() {
System.out.println("火车站卖票");
}
}
ProxyFactory
package com.zhuang.proxy.cglib_proxy;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @Classname ProxyFactory
* @Description 代理工厂 实现MethodInterceptor
* @Date 2021/3/26 8:32
* @Created by dell
*/
public class ProxyFactory implements MethodInterceptor {
private TrainStation target = new TrainStation();
public TrainStation getProxyObject() {
//创建Enhancer对象 类似于JDK动态代理
Enhancer enhancer = new Enhancer();
//设置父类的字节码对象
enhancer.setSuperclass(target.getClass());
//设置回调函数
enhancer.setCallback(this);
//创建代理对象
TrainStation obj = (TrainStation) enhancer.create();
return obj;
}
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("代收点收取一些代理费用(CGLIB动态代理方式)");
Object result = methodProxy.invokeSuper(o, args);
return result;
}
}
Client
package com.zhuang.proxy.cglib_proxy;
/**
* @Classname Client
* @Description CGLIB动态代理模式 测试类
* @Date 2021/3/26 8:42
* @Created by dell
*/
public class Client {
public static void main(String[] args) {
//创建代理工厂对象
ProxyFactory factory = new ProxyFactory();
//获取代理对象
TrainStation proxyObject = factory.getProxyObject();
proxyObject.sell();
}
}
jdk代理和CGLIB代理
使用CGLib实现动态代理,CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,在JDK1.6之前比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的类或者方法进行代理,因为CGLib原理是动态生成被代理类的子类。
在JDK1.6、JDK1.7、JDK1.8逐步对JDK动态代理优化之后,在调用次数较少的情况下,JDK代理效率高于CGLib代理效率,只有当进行大量调用的时候,JDK1.6和JDK1.7比CGLib代理效率低一点,但是到JDK1.8的时候,JDK代理效率高于CGLib代理。所以如果有接口使用JDK动态代理,如果没有接口使用CGLIB代理。
动态代理和静态代理
动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。
如果接口增加一个方法,静态代理模式除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。而动态代理不会出现该问题
远程(Remote)代理
本地服务通过网络请求远程服务。为了实现本地到远程的通信,我们需要实现网络通信,处理其中可能的异常。为良好的代码设计和可维护性,我们将网络通信部分隐藏起来,只暴露给本地服务一个接口,通过该接口即可访问远程服务提供的功能,而不必过多关心通信部分的细节。
防火墙(Firewall)代理
当你将浏览器配置成使用代理功能时,防火墙就将你的浏览器的请求转给互联网;当互联网返回响应时,代理服务器再把它转给你的浏览器。
保护(Protect or Access)代理
控制对一个对象的访问,如果需要,可以给不同的用户提供不同级别的使用权限。
命令(Command)模式的定义如下:**将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。**这样两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递、调用、增加与管理。
命令模式的主要优点如下。
其缺点是:
关系类图
Command
package com.zhuang.command;
/**
* @Classname Command
* @Description 抽象命令类
* @Date 2021/3/27 10:25
* @Created by dell
*/
public interface Command {
void execute(); // 只需要定义一个统一的执行方法
}
OrderCommand
package com.zhuang.command;
/**
* @Classname OrderCommand
* @Description 具体命令类
* @Date 2021/3/27 10:25
* @Created by dell
*/
public class OrderCommand implements Command{
//持有接受者对象
private SeniorChef receiver;
private Order order;
public OrderCommand(SeniorChef receiver, Order order) {
this.receiver = receiver;
this.order = order;
}
@Override
public void execute() {
System.out.println(order.getDiningTable()+"桌的订单:");
for (String key : order.getFoodDic().keySet()) {
receiver.makefood(order.getFoodDic().get(key),key);
}
try {
Thread.sleep(1000); //模拟做饭 睡眠1秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(order.getDiningTable()+"桌的饭弄好了");
}
}
Order
package com.zhuang.command;
import java.util.HashMap;
import java.util.Map;
/**
* @Classname Order
* @Description 订单类
* @Date 2021/3/27 10:34
* @Created by dell
*/
public class Order {
// 餐桌号码
private int diningTable;
//用来存储餐名并记录
private Map<String, Integer> foodDic = new HashMap<String, Integer>();
public int getDiningTable() {
return diningTable;
}
public void setDiningTable(int diningTable) {
this.diningTable = diningTable;
}
public Map<String, Integer> getFoodDic() {
return foodDic;
}
public void setFoodDic(String name, int num) {
foodDic.put(name, num);
}
}
SeniorChef
package com.zhuang.command;
/**
* @Classname SeniorChef
* @Description 厨师类
* @Date 2021/3/27 10:27
* @Created by dell
*/
public class SeniorChef {
//大厨师类 是命令的Receiver
public void makefood(int num, String foodName) {
System.out.println(num + "份" + foodName);
}
}
Waitor
package com.zhuang.command;
import java.util.ArrayList;
/**
* @Classname Waitor
* @Description 服务员类
* @Date 2021/3/27 10:30
* @Created by dell
*/
public class Waitor {
//可以持有很多命令对象
private ArrayList<Command> commands;
public Waitor() {
commands = new ArrayList<Command>();
}
public void setCommands(Command cmd) {
commands.add(cmd);
}
//发出命令 订单来了 大厨师开始执行命令
public void orderUp() {
System.out.println("来活了...");
for (int i = 0; i < commands.size(); i++) {
Command cmd = commands.get(i);
if (cmd != null) {
cmd.execute();
}
}
}
}
Client
package com.zhuang.command;
/**
* @Classname Client
* @Description 命令模式 测试类
* @Date 2021/3/27 10:44
* @Created by dell
*/
public class Client {
public static void main(String[] args) {
//创建order
Order order1 = new Order();
order1.setDiningTable(1);
order1.getFoodDic().put("西红柿炒鸡蛋", 1);
order1.getFoodDic().put("罐装可乐", 2);
Order order2 = new Order();
order2.setDiningTable(2);
order2.getFoodDic().put("酸溜土豆丝", 1);
order2.getFoodDic().put("王老吉", 1);
//创建接受者
SeniorChef receiver = new SeniorChef();
//将订单和接受者封装成命令对象
OrderCommand cmd1 = new OrderCommand(receiver, order1);
OrderCommand cmd2 = new OrderCommand(receiver, order2);
//创建调用者 waitor
Waitor invoke = new Waitor();
invoke.setCommands(cmd1);
invoke.setCommands(cmd2);
//将订单给柜台 呼叫厨师
invoke.orderUp();
}
}
Runable是一个典型命令模式,Runnable担当命令的角色,Thread充当的是调用者,start方法就是其执行方法
//命令接口(抽象命令角色)
public interface Runnable {
public abstract void run();
}
//调用者
public class Thread implements Runnable {
private Runnable target;
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}
private native void start0();
}
会调用一个native方法start0(),调用系统方法,开启一个线程。而接收者是对程序员开放的,可以自己定义接收者。
/**
* jdk Runnable 命令模式
* TurnOffThread : 属于具体
*/
public class TurnOffThread implements Runnable{
private Receiver receiver;
public TurnOffThread(Receiver receiver) {
this.receiver = receiver;
}
public void run() {
receiver.turnOFF();
}
}
/**
* 测试类
*/
public class Demo {
public static void main(String[] args) {
Receiver receiver = new Receiver();
TurnOffThread turnOffThread = new TurnOffThread(receiver);
Thread thread = new Thread(turnOffThread);
thread.start();
}
}
访问者(Visitor)模式的定义:**将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。**它将对数据的操作与数据结构进行分离,是行为类模式中最复杂的一种模式。
访问者(Visitor)模式是一种对象行为型模式,其主要优点如下。
访问者(Visitor)模式的主要缺点如下。
现在养宠物的人特别多,我们就以这个为例,当然宠物还分为狗,猫等,要给宠物喂食的话,主人可以喂,其他人也可以喂食。
Person
package com.zhuang.visitor;
/**
* @Classname Person
* @Description 抽象访问者接口
* @Date 2021/3/27 16:48
* @Created by dell
*/
public interface Person {
//喂宠物狗
void feed(Dog dog);
//喂宠物猫
void feed(Cat cat);
}
Owner
package com.zhuang.visitor;
/**
* @Classname Owner
* @Description 具体访问者角色 主人类
* @Date 2021/3/27 16:49
* @Created by dell
*/
public class Owner implements Person {
@Override
public void feed(Dog dog) {
System.out.println("主人喂食宠物狗...");
}
@Override
public void feed(Cat cat) {
System.out.println("主人喂食宠物猫...");
}
}
Someone
package com.zhuang.visitor;
/**
* @Classname Someone
* @Description 具体访问者角色 其他人类
* @Date 2021/3/27 16:49
* @Created by dell
*/
public class Someone implements Person {
@Override
public void feed(Dog dog) {
System.out.println("其他人喂食宠物狗...");
}
@Override
public void feed(Cat cat) {
System.out.println("其他人喂食宠物猫...");
}
}
Animal
package com.zhuang.visitor;
/**
* @Classname Animal
* @Description 定义抽象节点
* @Date 2021/3/27 16:50
* @Created by dell
*/
public interface Animal {
void accept(Person person);
}
Dog
package com.zhuang.visitor;
/**
* @Classname Dog
* @Description 具体节点 实现Animal接口
* @Date 2021/3/27 16:48
* @Created by dell
*/
public class Dog implements Animal {
@Override
public void accept(Person person) {
person.feed(this);
System.out.println("真香~,汪汪汪!!!");
}
}
Cat
package com.zhuang.visitor;
/**
* @Classname Cat
* @Description 用一句话描述类的作用
* @Date 2021/3/27 16:49
* @Created by dell
*/
public class Cat implements Animal {
@Override
public void accept(Person person) {
person.feed(this);
System.out.println("真香~,喵喵喵!!!");
}
}
Home
package com.zhuang.visitor;
import java.util.ArrayList;
import java.util.List;
/**
* @Classname Home
* @Description 定义对象结构 主人的家
* @Date 2021/3/27 16:50
* @Created by dell
*/
public class Home {
private List<Animal> nodeList = new ArrayList<Animal>();
//添加方法
public void add(Animal animal) {
nodeList.add(animal);
}
public void aciton(Person person) {
for (Animal node : nodeList) {
node.accept(person);
}
}
}
Client
package com.zhuang.visitor;
/**
* @Classname Client
* @Description 访问者模式 测试类
* @Date 2021/3/27 16:50
* @Created by dell
*/
public class Client {
public static void main(String[] args) {
Home home = new Home();
home.add(new Dog());
home.add(new Cat());
Owner owner = new Owner();
home.aciton(owner);
System.out.println("===========================");
Someone someone = new Someone();
home.aciton(someone);
}
}
访问者模式用到了一种双分派的技术。
1,分派:
变量被声明时的类型叫做变量的静态类型,有些人又把静态类型叫做明显类型;而变量所引用的对象的真实类型又叫做变量的实际类型。比如 Map map = new HashMap()
,map变量的静态类型是 Map
,实际类型是 HashMap
。根据对象的类型而对方法进行的选择,就是分派(Dispatch),分派(Dispatch)又分为两种,即静态分派和动态分派。
静态分派(Static Dispatch) 发生在编译时期,分派根据静态类型信息发生。静态分派对于我们来说并不陌生,方法重载就是静态分派。
动态分派(Dynamic Dispatch) 发生在运行时期,动态分派动态地置换掉某个方法。Java通过方法的重写支持动态分派。
2,动态分派:
通过方法的重写支持动态分派。
public class Animal {
public void execute() {
System.out.println("Animal");
}
}
public class Dog extends Animal {
@Override
public void execute() {
System.out.println("我是狗...");
}
}
public class Cat extends Animal {
@Override
public void execute() {
System.out.println("我是猫...");
}
}
public class Client {
public static void main(String[] args) {
Animal a = new Dog();
a.execute();
Animal a1 = new Cat();
a1.execute();
}
}
上面代码的结果大家应该直接可以说出来,这不就是多态吗!运行执行的是子类中的方法。
Java编译器在编译时期并不总是知道哪些代码会被执行,因为编译器仅仅知道对象的静态类型,而不知道对象的真实类型;而方法的调用则是根据对象的真实类型,而不是静态类型。
3,静态分派:
通过方法重载支持静态分派。
public class Animal {
}
public class Dog extends Animal {
}
public class Cat extends Animal {
}
public class Execute {
public void execute(Animal a) {
System.out.println("Animal");
}
public void execute(Dog d) {
System.out.println("我是狗...");
}
public void execute(Cat c) {
System.out.println("我是猫...");
}
}
public class Client {
public static void main(String[] args) {
Animal a = new Animal();
Animal a1 = new Dog();
Animal a2 = new Cat();
Execute exe = new Execute();
exe.execute(a);
exe.execute(a1);
exe.execute(a2);
}
}
运行结果:
这个结果可能出乎一些人的意料了,为什么呢?
重载方法的分派是根据静态类型进行的,这个分派过程在编译时期就完成了。
4,双分派:
所谓双分派技术就是在选择一个方法的时候,不仅仅要根据消息接收者(receiver)的运行时区别,还要根据参数的运行时区别。
public class Animal {
public void accept(Execute exe) {
exe.execute(this);
}
}
public class Dog extends Animal {
public void accept(Execute exe) {
exe.execute(this);
}
}
public class Cat extends Animal {
public void accept(Execute exe) {
exe.execute(this);
}
}
public class Execute {
public void execute(Animal a) {
System.out.println("animal");
}
public void execute(Dog d) {
System.out.println("我是狗...");
}
public void execute(Cat c) {
System.out.println("我是猫...");
}
}
public class Client {
public static void main(String[] args) {
Animal a = new Animal();
Animal d = new Dog();
Animal c = new Cat();
Execute exe = new Execute();
a.accept(exe);
d.accept(exe);
c.accept(exe);
}
}
在上面代码中,客户端将Execute对象做为参数传递给Animal类型的变量调用的方法,这里完成第一次分派,这里是方法重写,所以是动态分派,也就是执行实际类型中的方法,同时也将自己this作为参数传递进去,这里就完成了第二次分派
,这里的Execute类中有多个重载的方法,而传递进行的是this,就是具体的实际类型的对象。
说到这里,我们已经明白双分派是怎么回事了,但是它有什么效果呢?就是可以实现方法的动态绑定,我们可以对上面的程序进行修改。
运行结果如下:
双分派实现动态绑定的本质,就是在重载方法委派的前面加上了继承体系中覆盖的环节,由于覆盖是动态的,所以重载就是动态的了。
迭代器(Iterator)模式的定义:**提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。**迭代器模式是一种对象行为型模式,其主要优点如下。
其主要缺点是:
定义一个可以存储学生对象的容器对象,将遍历该容器的功能交由迭代器实现
关系类图
Student
package com.zhuang.Iterator;
/**
* @Classname Student
* @Description 学生类
* @Date 2021/3/28 12:47
* @Created by dell
*/
public class Student {
private String name;
private String number;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public Student() {
}
public Student(String name, String number) {
this.name = name;
this.number = number;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", number='" + number + '\'' +
'}';
}
}
StudentIterator
package com.zhuang.Iterator;
/**
* @Classname StudentIterator
* @Description 迭代器接口
* @Date 2021/3/28 12:43
* @Created by dell
*/
public interface StudentIterator {
boolean hasNext();
Student next();
}
StudentIteratorImpl
package com.zhuang.Iterator;
import java.util.List;
/**
* @Classname StudentIteratorImpl
* @Description 具体的迭代器类
* @Date 2021/3/28 12:43
* @Created by dell
*/
public class StudentIteratorImpl implements StudentIterator {
private List<Student> list;
private int position = 0;
public StudentIteratorImpl(List<Student> list) {
this.list = list;
}
@Override
public boolean hasNext() {
return position < list.size();
}
@Override
public Student next() {
Student currentStudent = list.get(position);
position++;
return currentStudent;
}
}
StudentAggregate
package com.zhuang.Iterator;
/**
* @Classname StudentAggregate
* @Description 抽象容器类
* @Date 2021/3/28 12:44
* @Created by dell
*/
public interface StudentAggregate {
//添加学生的功能
void addStudent(Student student);
//删除学生的功能
void removeStudent(Student student);
//获取迭代器功能的对象
StudentIterator getStudentIterator();
}
StudentAggregateImpl
package com.zhuang.Iterator;
import java.util.ArrayList;
import java.util.List;
/**
* @Classname StudentAggregateImpl
* @Description 具体的容器类
* @Date 2021/3/28 12:44
* @Created by dell
*/
public class StudentAggregateImpl implements StudentAggregate {
private List<Student> list = new ArrayList<Student>();
@Override
public void addStudent(Student student) {
this.list.add(student);
}
@Override
public void removeStudent(Student student) {
this.list.remove(student);
}
@Override
public StudentIterator getStudentIterator() {
return new StudentIteratorImpl(list);
}
}
Client
package com.zhuang.Iterator;
/**
* @Classname Client
* @Description 迭代器模式 测试类
* @Date 2021/3/28 12:54
* @Created by dell
*/
public class Client {
public static void main(String[] args) {
StudentAggregateImpl aggregate = new StudentAggregateImpl();
//添加元素
aggregate.addStudent(new Student("张三", "001"));
aggregate.addStudent(new Student("李四", "002"));
aggregate.addStudent(new Student("王五", "003"));
aggregate.addStudent(new Student("赵六", "004"));
aggregate.addStudent(new Student("田七", "005"));
//遍历聚合对象
StudentIterator iterator = aggregate.getStudentIterator();
//遍历
while (iterator.hasNext()) {
Student student = iterator.next();
System.out.println(student);
}
}
}
迭代器模式在JAVA的很多集合类中被广泛应用,接下来看看JAVA源码中是如何使用迭代器模式的。
List<String> list = new ArrayList<>();
Iterator<String> iterator = list.iterator(); //list.iterator()方法返回的肯定是Iterator接口的子实现类对象
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
看完这段代码是不是很熟悉,与我们上面代码基本类似。单列集合都使用到了迭代器,我们以ArrayList举例来说明
Iterator
接口的具体迭代器对象具体的来看看 ArrayList的代码实现
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
int cursor; // 下一个要返回元素的索引
int lastRet = -1; // 上一个返回元素的索引
int expectedModCount = modCount;
Itr() {}
//判断是否还有元素
public boolean hasNext() {
return cursor != size;
}
//获取下一个元素
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
...
}
这部分代码还是比较简单,大致就是在 iterator
方法中返回了一个实例化的 Iterator
对象。Itr是一个内部类,它实现了 Iterator
接口并重写了其中的抽象方法。
注意:
当我们在使用JAVA开发的时候,想使用迭代器模式的话,只要让我们自己定义的容器类实现
java.util.Iterable
并实现其中的iterator()方法使其返回一个java.util.Iterator
的实现类就可以了。
观察者(Observer)模式的定义:**指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。**这种模式有时又称作发布-订阅模式、模型-视图模式,它是对象行为型模式。
观察者模式是一种对象行为型模式,其主要优点如下。
它的主要缺点如下。
实现观察者模式时要注意具体目标对象和具体观察者对象之间不能直接调用,否则将使两者之间紧密耦合起来,这违反了面向对象的设计原则
在使用微信公众号时,大家都会有这样的体验,当你关注的公众号中有新内容更新的话,它就会推送给关注公众号的微信用户端。我们使用观察者模式来模拟这样的场景,微信用户就是观察者,微信公众号是被观察者,有多个的微信用户关注了程序猿这个公众号
关系类图
Observer
package com.zhuang.observer;
/**
* @Classname Observer
* @Description 抽象观察者
* @Date 2021/3/28 14:14
* @Created by dell
*/
public interface Observer {
//更新的方法
void update(String messages);
}
WexinUser
package com.zhuang.observer;
/**
* @Classname WexinUser
* @Description 具体观察者类 实现更新的方法
* @Date 2021/3/28 14:14
* @Created by dell
*/
public class WexinUser implements Observer {
//用户名
private String name;
public WexinUser(String name) {
this.name = name;
}
public WexinUser() {
}
@Override
public void update(String messages) {
System.out.println(name + "-->" + messages);
}
}
Subject
package com.zhuang.observer;
/**
* @Classname Subject
* @Description 抽象主题类
* @Date 2021/3/28 14:15
* @Created by dell
*/
public interface Subject {
//增加订阅者
public void attach(Observer observer);
//删除订阅者
public void remove(Observer observer);
//通知订阅者更新消息
public void notify(String messages);
}
SubscriptionSubject
package com.zhuang.observer;
import java.util.ArrayList;
import java.util.List;
/**
* @Classname SubscriptionSubject
* @Description 具体主题(具体被观察者)
* @Date 2021/3/28 14:15
* @Created by dell
*/
public class SubscriptionSubject implements Subject {
//存储订阅公众号的微信用户
private List<Observer> weixinUserList = new ArrayList<Observer>();
@Override
public void attach(Observer observer) {
weixinUserList.add(observer);
}
@Override
public void remove(Observer observer) {
weixinUserList.remove(observer);
}
@Override
public void notify(String messages) {
for (Observer observer : weixinUserList) {
observer.update(messages);
}
}
}
Client
package com.zhuang.observer;
/**
* @Classname Client
* @Description 观察者模式 测试类
* @Date 2021/3/28 14:16
* @Created by dell
*/
public class Client {
public static void main(String[] args) {
SubscriptionSubject subject = new SubscriptionSubject();
//创建微信用户
WexinUser user1 = new WexinUser("张三");
WexinUser user2 = new WexinUser("李四");
WexinUser user3 = new WexinUser("王五");
//订阅公众号
subject.attach(user1);
subject.attach(user2);
subject.attach(user3);
//通过订阅用户
subject.notify("您关注的公众号更新啦~~~");
}
}
在 Java 中,通过 java.util.Observable 类和 java.util.Observer 接口定义了观察者模式,只要实现它们的子类就可以编写观察者模式实例。
1,Observable类
Observable 类是抽象目标类(被观察者),它有一个 Vector 集合成员变量,用于保存所有要通知的观察者对象,下面来介绍它最重要的 3 个方法。
void addObserver(Observer o) 方法:用于将新的观察者对象添加到集合中。
void notifyObservers(Object arg) 方法:调用集合中的所有观察者对象的 update方法,通知它们数据发生改变。通常越晚加入集合的观察者越先得到通知。
void setChange() 方法:用来设置一个 boolean 类型的内部标志,注明目标对象发生了变化。当它为true时,notifyObservers() 才会通知观察者。
2,Observer 接口
Observer 接口是抽象观察者,它监视目标对象的变化,当目标对象发生变化时,观察者得到通知,并调用 update 方法,进行相应的工作。
【例】警察抓小偷
警察抓小偷也可以使用观察者模式来实现,警察是观察者,小偷是被观察者。代码如下:
小偷是一个被观察者,所以需要继承Observable类
package com.zhuang.observer.observable_example;
import java.util.Observable;
/**
* @Classname Thief
* @Description 小偷类 继承Observable接口
* @Date 2021/3/28 14:36
* @Created by dell
*/
public class Thief extends Observable {
private String name;
public Thief(String name) {
this.name = name;
}
public Thief() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void steal() {
System.out.println("小偷:我偷东西,有没有人来抓我!!!");
super.setChanged();//默认为true
super.notifyObservers();
}
}
警察是一个观察者,所以需要让其实现Observer接口
package com.zhuang.observer.observable_example;
import java.util.Observable;
import java.util.Observer;
/**
* @Classname Policemen
* @Description 警察类 实现Observe类 实现update方法
* @Date 2021/3/28 14:36
* @Created by dell
*/
public class Policemen implements Observer {
private String name;
public Policemen(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Policemen() {
}
@Override
public void update(Observable o, Object arg) {
System.out.println("警察:" + ((Thief) o).getName() + "你被我抓到了哈哈哈哈!!!");
}
}
客户端代码
package com.zhuang.observer.observable_example;
/**
* @Classname Client
* @Description 测试类
* @Date 2021/3/28 14:36
* @Created by dell
*/
public class Client {
public static void main(String[] args) {
//小偷对象
Thief thief = new Thief("法外狂徒张三");
//警察对象
Policemen policemen = new Policemen("小庄警察");
//警察盯着小偷
thief.addObserver(policemen);
//小偷行窃
thief.steal();
}
}
一般来说,同事类之间的关系是比较复杂的,多个同事类之间互相关联时,他们之间的关系会呈现为复杂的网状结构,这是一种过度耦合的架构,即不利于类的复用,也不稳定。例如在下左图中,有六个同事类对象,假如对象1发生变化,那么将会有4个对象受到影响。如果对象2发生变化,那么将会有5个对象受到影响。也就是说,同事类之间直接关联的设计是不好的。
如果引入中介者模式,那么同事类之间的关系将变为星型结构,从下右图中可以看到,任何一个类的变动,只会影响的类本身,以及中介者,这样就减小了系统的耦合。一个好的设计,必定不会把所有的对象关系处理逻辑封装在本类中,而是使用一个专门的类来管理那些不属于自己的行为。
<img alt="">
中介者(Mediator)模式的定义:定义一个中介对象来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变它们之间的交互。中介者模式又叫调停模式,它是迪米特法则的典型应用。
中介者模式是一种对象行为型模式,其主要优点如下。
其主要缺点是:中介者模式将原本多个对象直接的相互依赖变成了中介者和多个同事类的依赖关系。当同事类越多时,中介者就会越臃肿,变得复杂且难以维护。
现在租房基本都是通过房屋中介,房主将房屋托管给房屋中介,而租房者从房屋中介获取房屋信息。房屋中介充当租房者与房屋所有者之间的中介者
关系类图
Mediator
package com.zhuang.mediator;
/**
* @Classname Mediator
* @Description 抽象中介者
* @Date 2021/3/28 20:45
* @Created by dell
*/
public abstract class Mediator {
//声明一个联络方法
public abstract void constact(String message, Person person);
}
Person
package com.zhuang.mediator;
/**
* @Classname Person
* @Description 抽象同事类
* @Date 2021/3/28 20:45
* @Created by dell
*/
public abstract class Person {
protected String name;
protected Mediator mediator;
public Person(String name, Mediator mediator) {
this.name = name;
this.mediator = mediator;
}
}
HouseOwner
package com.zhuang.mediator;
/**
* @Classname HouseOwner
* @Description 具体同事类 房屋拥有者
* @Date 2021/3/28 20:45
* @Created by dell
*/
public class HouseOwner extends Person{
public HouseOwner(String name, Mediator mediator) {
super(name, mediator);
}
//与中介者联系
public void constact(String message){
mediator.constact(message,this);
}
//获取信息
public void getMessage(String message){
System.out.println("房主"+name+"获取的信息:"+message);
}
}
Tenant
package com.zhuang.mediator;
/**
* @Classname Tenant
* @Description 具体同事类 承租人
* @Date 2021/3/28 20:46
* @Created by dell
*/
public class Tenant extends Person{
public Tenant(String name, Mediator mediator) {
super(name, mediator);
}
//与中介者联系
public void constact(String message){
mediator.constact(message,this);
}
//获取信息
//获取信息
public void getMessage(String message){
System.out.println("租房者"+name+"获取的信息:"+message);
}
}
MediatorStructure
package com.zhuang.mediator;
/**
* @Classname MediatorStructure
* @Description 中介机构
* @Date 2021/3/28 20:46
* @Created by dell
*/
public class MediatorStructure extends Mediator {
//中介结构必须知道所有房主和租房者的信息
private HouseOwner houseOwner;
private Tenant tenant;
public HouseOwner getHouseOwner() {
return houseOwner;
}
public void setHouseOwner(HouseOwner houseOwner) {
this.houseOwner = houseOwner;
}
public Tenant getTenant() {
return tenant;
}
public void setTenant(Tenant tenant) {
this.tenant = tenant;
}
@Override
public void constact(String message, Person person) {
if (person == houseOwner) {
//如果是房主,则租房者获得信息
tenant.getMessage(message);
} else {
//反之是房主获得信息
houseOwner.getMessage(message);
}
}
}
Client
package com.zhuang.mediator;
/**
* @Classname Client
* @Description 中介者模式 测试类
* @Date 2021/3/28 20:47
* @Created by dell
*/
public class Client {
public static void main(String[] args) {
//房主 租房者 中介机构
MediatorStructure mediator = new MediatorStructure();
//房主和租房者只需要知道中介机构即可
HouseOwner houseOwner = new HouseOwner("张三", mediator);
Tenant tenant = new Tenant("李四", mediator);
//中介机构需要知道房主和租房者
mediator.setHouseOwner(houseOwner);
mediator.setTenant(tenant);
tenant.constact("需要租一间房子");
houseOwner.constact("我有一间房子,你要租吗???");
}
}
备忘录(Memento)模式的定义:**在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。**该模式又叫快照模式。
备忘录模式是一种对象行为型模式,其主要优点如下。
其主要缺点是:
游戏中的某个场景,一游戏角色有生命力、攻击力、防御力等数据,在打Boss前和后一定会不一样的,我们允许玩家如果感觉与Boss决斗的效果不理想可以让游戏恢复到决斗之前的状态。
要实现上述案例,有两种方式:
备忘录角色对任何对象都提供一个接口,即宽接口,备忘录角色的内部所存储的状态就对所有对象公开
关系类图
GameRole
package com.zhuang.memento.white_box;
/**
* @Classname GameRole
* @Description 游戏角色类
* @Date 2021/3/29 10:14
* @Created by dell
*/
public class GameRole {
private int vit;//生命力
private int atk;//攻击力
private int def;//防御力
//初始化状态
public void initState() {
this.vit = 100;
this.atk = 100;
this.def = 100;
}
//战斗
public void fight() {
this.vit = 0;
this.atk = 0;
this.def = 0;
}
//保存角色状态
public RoleStateMemento saveState() {
return new RoleStateMemento(vit, atk, def);
}
//恢复角色状态
public void recoverState(RoleStateMemento roleStateMemento) {
this.vit = roleStateMemento.getVit();
this.atk = roleStateMemento.getAtk();
this.def = roleStateMemento.getDef();
}
//展示状态
public void stateDisplay() {
System.out.println("角色生命力" + vit);
System.out.println("角色攻击力" + atk);
System.out.println("角色防御力" + def);
}
public int getVit() {
return vit;
}
public void setVit(int vit) {
this.vit = vit;
}
public int getAtk() {
return atk;
}
public void setAtk(int atk) {
this.atk = atk;
}
public int getDef() {
return def;
}
public void setDef(int def) {
this.def = def;
}
}
RoleStateMemento
package com.zhuang.memento.white_box;
/**
* @Classname RoleStateMemento
* @Description 游戏状态存储类 备忘录类
* @Date 2021/3/29 10:14
* @Created by dell
*/
public class RoleStateMemento {
private int vit;
private int atk;
private int def;
public RoleStateMemento(int vit, int atk, int def) {
this.vit = vit;
this.atk = atk;
this.def = def;
}
public int getVit() {
return vit;
}
public void setVit(int vit) {
this.vit = vit;
}
public int getAtk() {
return atk;
}
public void setAtk(int atk) {
this.atk = atk;
}
public int getDef() {
return def;
}
public void setDef(int def) {
this.def = def;
}
}
RoleStateCaretaker
package com.zhuang.memento.white_box;
/**
* @Classname RoleStateCaretaker
* @Description 角色状态管理类
* @Date 2021/3/29 10:15
* @Created by dell
*/
public class RoleStateCaretaker {
private RoleStateMemento roleStateMemento;
public RoleStateMemento getRoleStateMemento() {
return roleStateMemento;
}
public void setRoleStateMemento(RoleStateMemento roleStateMemento) {
this.roleStateMemento = roleStateMemento;
}
}
Client
package com.zhuang.memento.white_box;
/**
* @Classname Client
* @Description 备忘录模式 白箱 测试类
* @Date 2021/3/29 10:15
* @Created by dell
*/
public class Client {
public static void main(String[] args) {
System.out.println("--------------大战Boss前------------------------");
//大战Boss前
GameRole gameRole = new GameRole();
gameRole.initState();
gameRole.stateDisplay();
//保存进度
RoleStateCaretaker roleStateCaretaker = new RoleStateCaretaker();
roleStateCaretaker.setRoleStateMemento(gameRole.saveState());
System.out.println("--------------大战Boss后------------------------");
//大战Boss 损耗严重
gameRole.fight();
gameRole.stateDisplay();
System.out.println("--------------满血复活------------------------");
gameRole.recoverState(roleStateCaretaker.getRoleStateMemento());
gameRole.stateDisplay();
}
}
备忘录角色对发起人对象提供一个宽接口,而为其他对象提供一个窄接口。在Java语言中,实现双重接口的办法就是将备忘录类设计成发起人类的内部成员类。
将 RoleStateMemento
设为 GameRole
的内部类,从而将 RoleStateMemento
对象封装在 GameRole
里面;在外面提供一个标识接口 Memento
给 RoleStateCaretaker
及其他对象使用。这样 GameRole
类看到的是 RoleStateMemento
所有的接口,而RoleStateCaretaker
及其他对象看到的仅仅是标识接口 Memento
所暴露出来的接口,从而维护了封装型。类图如下:
GameRole
package com.zhuang.memento.black_box;
import com.zhuang.memento.white_box.RoleStateMemento;
/**
* @Classname GameRole
* @Description 游戏角色类
* @Date 2021/3/29 10:14
* @Created by dell
*/
public class GameRole {
private int vit;//生命力
private int atk;//攻击力
private int def;//防御力
//初始化状态
public void initState() {
this.vit = 100;
this.atk = 100;
this.def = 100;
}
//战斗
public void fight() {
this.vit = 0;
this.atk = 0;
this.def = 0;
}
//保存角色状态
public Memento saveState() {
return new RoleStateMemento(vit, atk, def);
}
//恢复角色状态
public void recoverState(Memento memento) {
RoleStateMemento roleStateMemento = (RoleStateMemento) memento;
this.vit = roleStateMemento.getVit();
this.atk = roleStateMemento.getAtk();
this.def = roleStateMemento.getDef();
}
//展示状态
public void stateDisplay() {
System.out.println("角色生命力" + vit);
System.out.println("角色攻击力" + atk);
System.out.println("角色防御力" + def);
}
public int getVit() {
return vit;
}
public void setVit(int vit) {
this.vit = vit;
}
public int getAtk() {
return atk;
}
public void setAtk(int atk) {
this.atk = atk;
}
public int getDef() {
return def;
}
public void setDef(int def) {
this.def = def;
}
//在内部定义备忘录内部类 RoleStateMemento(该内部类设置为私有的)
private class RoleStateMemento implements Memento {
private int vit;
private int atk;
private int def;
public RoleStateMemento(int vit, int atk, int def) {
this.vit = vit;
this.atk = atk;
this.def = def;
}
public int getVit() {
return vit;
}
public void setVit(int vit) {
this.vit = vit;
}
public int getAtk() {
return atk;
}
public void setAtk(int atk) {
this.atk = atk;
}
public int getDef() {
return def;
}
public void setDef(int def) {
this.def = def;
}
}
}
Memento
package com.zhuang.memento.black_box;
/**
* @Classname Memento
* @Description 窄接口
* @Date 2021/3/29 10:40
* @Created by dell
*/
public interface Memento {
}
RoleStateCaretaker
package com.zhuang.memento.black_box;
/**
* @Classname RoleStateCaretaker
* @Description 角色状态管理类
* @Date 2021/3/29 10:15
* @Created by dell
*/
public class RoleStateCaretaker {
private Memento memento;
public Memento getMemento() {
return memento;
}
public void setMemento(Memento memento) {
this.memento = memento;
}
}
Client
package com.zhuang.memento.black_box;
/**
* @Classname Client
* @Description 备忘录模式 黑箱 测试类
* @Date 2021/3/29 10:15
* @Created by dell
*/
public class Client {
public static void main(String[] args) {
System.out.println("--------------大战Boss前------------------------");
//大战Boss前
GameRole gameRole = new GameRole();
gameRole.initState();
gameRole.stateDisplay();
//保存进度
RoleStateCaretaker roleStateCaretaker = new RoleStateCaretaker();
roleStateCaretaker.setMemento(gameRole.saveState());
System.out.println("--------------大战Boss后------------------------");
//大战Boss 损耗严重
gameRole.fight();
gameRole.stateDisplay();
System.out.println("--------------满血复活------------------------");
gameRole.recoverState(roleStateCaretaker.getMemento());
gameRole.stateDisplay();
}
}
设计一个软件用来进行加减计算。我们第一想法就是使用工具类,提供对应的加法和减法的工具方法。
//用于两个整数相加
public static int add(int a,int b){
return a + b;
}
//用于两个整数相加
public static int add(int a,int b,int c){
return a + b + c;
}
//用于n个整数相加
public static int add(Integer ... arr) {
int sum = 0;
for (Integer i : arr) {
sum += i;
}
return sum;
}
上面的形式比较单一、有限,如果形式变化非常多,这就不符合要求,因为加法和减法运算,两个运算符与数值可以有无限种组合方式。比如 1+2+3+4+5、1+2+3-4等等。
显然,现在需要一种翻译识别机器,能够解析由数字以及 + - 符号构成的合法的运算序列。如果把运算符和数字都看作节点的话,能够逐个节点的进行读取解析运算,这就是解释器模式的思维。
解释器(Interpreter)模式的定义:**给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句子。**也就是说,用编译语言的方式来分析应用中的实例。这种模式实现了文法表达式处理的接口,该接口解释一个特定的上下文。
这里提到的文法和句子的概念同编译原理中的描述相同,“文法”指语言的语法规则,而“句子”是语言集中的元素。例如,汉语中的句子有很多,“我是中国人”是其中的一个句子,可以用一棵语法树来直观地描述语言中的句子。
解释器模式是一种类行为型模式,其主要优点如下。
解释器模式的主要缺点如下。
关系类图
AbstractExpression
package com.zhuang.interpreter;
/**
* @Classname AbstractExpression
* @Description 抽象角色
* @Date 2021/3/31 9:45
* @Created by dell
*/
public abstract class AbstractExpression {
public abstract int interpret(Context context);
}
Value
package com.zhuang.interpreter;
/**
* @Classname Value
* @Description 终结符表达式角色
* @Date 2021/3/31 9:48
* @Created by dell
*/
public class Value extends AbstractExpression {
private int value;
public Value(int value) {
this.value = value;
}
@Override
public int interpret(Context context) {
return value;
}
@Override
public String toString() {
return new Integer(value).toString();
}
}
Plus
package com.zhuang.interpreter;
/**
* @Classname Plus
* @Description 非终结符表达式角色 加法表达式
* @Date 2021/3/31 9:46
* @Created by dell
*/
public class Plus extends AbstractExpression {
private AbstractExpression left;
private AbstractExpression right;
public Plus(AbstractExpression left, AbstractExpression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret(Context context) {
return left.interpret(context) + right.interpret(context);
}
@Override
public String toString() {
return "(" + left.toString() + " + " + right.toString() + ")";
}
}
Minus
package com.zhuang.interpreter;
/**
* @Classname Minus
* @Description 非终结符表达式角色 减法表达式
* @Date 2021/3/31 9:46
* @Created by dell
*/
public class Minus extends AbstractExpression {
private AbstractExpression left;
private AbstractExpression right;
public Minus(AbstractExpression left, AbstractExpression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret(Context context) {
return left.interpret(context) - right.interpret(context);
}
@Override
public String toString() {
return "(" + left.toString() + " - " + right.toString() + ")";
}
}
Variable
package com.zhuang.interpreter;
/**
* @Classname Variable
* @Description 终结符表达式角色 变量表达式
* @Date 2021/3/31 9:46
* @Created by dell
*/
public class Variable extends AbstractExpression {
private String name;
public Variable(String name) {
this.name = name;
}
@Override
public int interpret(Context context) {
return context.getValue(this);
}
@Override
public String toString() {
return name;
}
}
Context
package com.zhuang.interpreter;
import java.util.HashMap;
import java.util.Map;
/**
* @Classname Context
* @Description 环境类
* @Date 2021/3/31 9:46
* @Created by dell
*/
public class Context {
private Map<Variable, Integer> map = new HashMap<Variable, Integer>();
public void assign(Variable var, Integer value) {
map.put(var, value);
}
public int getValue(Variable var) {
Integer value = map.get(var);
return value;
}
}
Client
package com.zhuang.interpreter;
/**
* @Classname Client
* @Description 解释权模式 测试类
* @Date 2021/3/31 9:46
* @Created by dell
*/
public class Client {
public static void main(String[] args) {
Context context = new Context();
Variable a = new Variable("a");
Variable b = new Variable("b");
Variable c = new Variable("c");
Variable d = new Variable("d");
Variable e = new Variable("e");
context.assign(a, 2);
context.assign(b, 3);
context.assign(c, 4);
context.assign(d, 5);
context.assign(e, 6);
AbstractExpression expression = new Minus(new Plus(new Plus(new Plus(a, b), c), d), e);
System.out.println(expression + "=" + expression.interpret(context));
}
}
状态(State)模式的定义:对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。
状态模式是一种对象行为型模式,其主要优点如下。
状态模式的主要缺点如下。
【例】通过按钮来控制一个电梯的状态,一个电梯有开门状态,关门状态,停止状态,运行状态。每一种状态改变,都有可能要根据其他状态来更新处理。例如,如果电梯门现在处于运行时状态,就不能进行开门操作,而如果电梯门是停止状态,就可以执行开门操作
public interface ILift {
//电梯的4个状态
//开门状态
public final static int OPENING_STATE = 1;
//关门状态
public final static int CLOSING_STATE = 2;
//运行状态
public final static int RUNNING_STATE = 3;
//停止状态
public final static int STOPPING_STATE = 4;
//设置电梯的状态
public void setState(int state);
//电梯的动作
public void open();
public void close();
public void run();
public void stop();
}
public class Lift implements ILift {
private int state;
@Override
public void setState(int state) {
this.state = state;
}
//执行关门动作
@Override
public void close() {
switch (this.state) {
case OPENING_STATE:
System.out.println("电梯关门了。。。");//只有开门状态可以关闭电梯门,可以对应电梯状态表来看
this.setState(CLOSING_STATE);//关门之后电梯就是关闭状态了
break;
case CLOSING_STATE:
//do nothing //已经是关门状态,不能关门
break;
case RUNNING_STATE:
//do nothing //运行时电梯门是关着的,不能关门
break;
case STOPPING_STATE:
//do nothing //停止时电梯也是关着的,不能关门
break;
}
}
//执行开门动作
@Override
public void open() {
switch (this.state) {
case OPENING_STATE://门已经开了,不能再开门了
//do nothing
break;
case CLOSING_STATE://关门状态,门打开:
System.out.println("电梯门打开了。。。");
this.setState(OPENING_STATE);
break;
case RUNNING_STATE:
//do nothing 运行时电梯不能开门
break;
case STOPPING_STATE:
System.out.println("电梯门开了。。。");//电梯停了,可以开门了
this.setState(OPENING_STATE);
break;
}
}
//执行运行动作
@Override
public void run() {
switch (this.state) {
case OPENING_STATE://电梯不能开着门就走
//do nothing
break;
case CLOSING_STATE://门关了,可以运行了
System.out.println("电梯开始运行了。。。");
this.setState(RUNNING_STATE);//现在是运行状态
break;
case RUNNING_STATE:
//do nothing 已经是运行状态了
break;
case STOPPING_STATE:
System.out.println("电梯开始运行了。。。");
this.setState(RUNNING_STATE);
break;
}
}
//执行停止动作
@Override
public void stop() {
switch (this.state) {
case OPENING_STATE: //开门的电梯已经是是停止的了(正常情况下)
//do nothing
break;
case CLOSING_STATE://关门时才可以停止
System.out.println("电梯停止了。。。");
this.setState(STOPPING_STATE);
break;
case RUNNING_STATE://运行时当然可以停止了
System.out.println("电梯停止了。。。");
this.setState(STOPPING_STATE);
break;
case STOPPING_STATE:
//do nothing
break;
}
}
}
public class Client {
public static void main(String[] args) {
Lift lift = new Lift();
lift.setState(ILift.STOPPING_STATE);//电梯是停止的
lift.open();//开门
lift.close();//关门
lift.run();//运行
lift.stop();//停止
}
}
问题分析
关系类图
LiftState
package com.zhuang.state.after;
/**
* @Classname LiftState
* @Description 抽象状态类
* @Date 2021/3/31 10:50
* @Created by dell
*/
public abstract class LiftState {
//定义一个环境角色,也就是封装状态的变化引起的功能变化
protected Context context;
public void setContext(Context context) {
this.context = context;
}
//电梯开门动作
public abstract void open();
//电梯关门动作
public abstract void close();
//电梯运行动作
public abstract void run();
//电梯停止动作
public abstract void stop();
}
Context
package com.zhuang.state.after;
/**
* @Classname Context
* @Description 定义所有电梯门状态
* @Date 2021/3/31 10:53
* @Created by dell
*/
public class Context {
//定义出所有的电梯状态
//开门状态,这时候电梯只能关闭
public final static OpeningState OPENNING_STATE = new OpeningState();
//关闭状态,这时候电梯可以运行、停止和开门
public final static ClosingState CLOSEING_STATE = new ClosingState();
//运行状态,这时候电梯只能停止
public final static RunningState RUNNING_STATE = new RunningState();
//停止状态,这时候电梯可以开门、运行
public final static StoppingState STOPPING_STATE = new StoppingState();
//定义一个当前电梯状态
private LiftState liftState;
public LiftState getLiftState() {
return this.liftState;
}
public void setLiftState(LiftState liftState) {
//当前环境改变
this.liftState = liftState;
//把当前的环境通知到各个实现类中
this.liftState.setContext(this);
}
public void open() {
this.liftState.open();
}
public void close() {
this.liftState.close();
}
public void run() {
this.liftState.run();
}
public void stop() {
this.liftState.stop();
}
}
OpeningState
package com.zhuang.state.after;
/**
* @Classname OpeningState
* @Description 开启状态
* @Date 2021/3/31 10:51
* @Created by dell
*/
public class OpeningState extends LiftState {
//开启当然可以关闭了,我就想测试一下电梯门开关功能
@Override
public void open() {
System.out.println("电梯门开启...");
}
@Override
public void close() {
//状态修改
super.context.setLiftState(Context.CLOSEING_STATE);
//动作委托为CloseState来执行,也就是委托给了ClosingState子类执行这个动作
super.context.getLiftState().close();
}
//电梯门不能开着就跑,这里什么也不做
@Override
public void run() {
//do nothing
}
//开门状态已经是停止的了
@Override
public void stop() {
//do nothing
}
}
ClosingState
package com.zhuang.state.after;
/**
* @Classname ClosingState
* @Description 关闭状态
* @Date 2021/3/31 10:52
* @Created by dell
*/
public class ClosingState extends LiftState {
@Override
//电梯门关闭,这是关闭状态要实现的动作
public void close() {
System.out.println("电梯门关闭...");
}
//电梯门关了再打开,逗你玩呢,那这个允许呀
@Override
public void open() {
super.context.setLiftState(Context.OPENNING_STATE);
super.context.open();
}
//电梯门关了就跑,这是再正常不过了
@Override
public void run() {
super.context.setLiftState(Context.RUNNING_STATE);
super.context.run();
}
//电梯门关着,我就不按楼层
@Override
public void stop() {
super.context.setLiftState(Context.STOPPING_STATE);
super.context.stop();
}
}
RunningState
package com.zhuang.state.after;
/**
* @Classname RunningState
* @Description 运行状态
* @Date 2021/3/31 10:52
* @Created by dell
*/
public class RunningState extends LiftState {
@Override
public void open() {
//什么也不做
}
@Override
public void close() {
//什么也不做
}
@Override
public void run() {
System.out.println("电梯正在运行...");
}
@Override
public void stop() {
//停止
super.context.setLiftState(Context.OPENNING_STATE);
super.context.stop();
}
}
StoppingState
package com.zhuang.state.after;
/**
* @Classname StoppingState
* @Description 停止状态
* @Date 2021/3/31 10:51
* @Created by dell
*/
public class StoppingState extends LiftState {
@Override
public void open() {
//状态修改
super.context.setLiftState(Context.OPENNING_STATE);
//动作委托给CloseState来执行 也就是委托给了ClosingState子类执行动作
super.context.getLiftState().open();
}
@Override
public void close() {
//状态修改
super.context.setLiftState(Context.CLOSEING_STATE);
//动作委托给CloseState来执行 也就是委托给了ClosingState子类执行动作
super.context.getLiftState().close();
}
@Override
public void run() {
//状态修改
super.context.setLiftState(Context.RUNNING_STATE);
//动作委托给CloseState来执行 也就是委托给了ClosingState子类执行动作
super.context.getLiftState().run();
}
@Override
public void stop() {
System.out.println("电梯停止了...");
}
}
Client
package com.zhuang.state.after;
/**
* @Classname Client
* @Description 状态模式 测试类
* @Date 2021/3/31 10:53
* @Created by dell
*/
public class Client {
public static void main(String[] args) {
//开门状态
System.out.println("开门状态-->");
Context context1 = new Context();
context1.setLiftState(new OpeningState());
context1.open();
context1.close();
context1.run();
context1.stop();
System.out.println("=========================");
//关门状态
System.out.println("关门状态-->");
Context context2 = new Context();
context2.setLiftState(new ClosingState());
context2.open();
context2.close();
context2.run();
context2.stop();
System.out.println("=========================");
//运行状态
System.out.println("运行状态-->");
Context context3 = new Context();
context3.setLiftState(new RunningState());
context3.open();
context3.close();
context3.run();
context3.stop();
System.out.println("=========================");
//停止状态
System.out.println("停止状态-->");
Context context4 = new Context();
context4.setLiftState(new StoppingState());
context4.open();
context4.close();
context4.run();
context4.stop();
}
}
策略(Strategy)模式的定义:**该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。**策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。
策略模式的主要优点如下。
其主要缺点如下。
针对不同节日不同的促销活动
关系类图
Strategy
package com.zhuang.strategy;
/**
* @Classname Strategy
* @Description 定义共同接口
* @Date 2021/3/31 15:29
* @Created by dell
*/
public interface Strategy {
void show();
}
StrategyA
package com.zhuang.strategy;
/**
* @Classname StrategyA
* @Description 定义具体策略角色 每个节日的具体促销活动
* @Date 2021/3/31 15:29
* @Created by dell
*/
public class StrategyA implements Strategy {
@Override
public void show() {
System.out.println("A促销 买一送一");
}
}
StrategyB
package com.zhuang.strategy;
/**
* @Classname StrategyB
* @Description 定义具体策略角色 每个节日的具体促销活动
* @Date 2021/3/31 15:30
* @Created by dell
*/
public class StrategyB implements Strategy {
@Override
public void show() {
System.out.println("B促销 满100减20");
}
}
StrategyC
package com.zhuang.strategy;
/**
* @Classname StrategyC
* @Description 定义具体策略角色 每个节日的具体促销活动
* @Date 2021/3/31 15:30
* @Created by dell
*/
public class StrategyC implements Strategy {
@Override
public void show() {
System.out.println("C促销 满500元可兑换小礼品");
}
}
SalesMan
package com.zhuang.strategy;
/**
* @Classname SalesMan
* @Description 定义环境角色 用于连接上下文 把促销活动推销给顾客
* @Date 2021/3/31 15:32
* @Created by dell
*/
public class SalesMan {
//持有抽象策略角色的引用
private Strategy strategy;
public SalesMan(Strategy strategy) {
this.strategy = strategy;
}
public Strategy getStrategy() {
return strategy;
}
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
//展示促销活动
public void salesManShow() {
strategy.show();
}
}
Client
package com.zhuang.strategy;
/**
* @Classname Client
* @Description 策略模式 测试类
* @Date 2021/3/31 15:34
* @Created by dell
*/
public class Client {
public static void main(String[] args) {
SalesMan salesMan = new SalesMan(new StrategyA());
//儿童节
salesMan.salesManShow();
System.out.println("=======================");
//劳动节
salesMan.setStrategy(new StrategyB());
salesMan.salesManShow();
System.out.println("=======================");
//端午节
salesMan.setStrategy(new StrategyC());
salesMan.salesManShow();
}
}
Comparator
中的策略模式。在Arrays类中有一个 sort()
方法,如下:
public class Arrays{
public static <T> void sort(T[] a, Comparator<? super T> c) {
if (c == null) {
sort(a);
} else {
if (LegacyMergeSort.userRequested)
legacyMergeSort(a, c);
else
TimSort.sort(a, 0, a.length, c, null, 0, 0);
}
}
}
Arrays就是一个环境角色类,这个sort方法可以传一个新策略让Arrays根据这个策略来进行排序。就比如下面的测试类。
public class demo {
public static void main(String[] args) {
Integer[] data = {12, 2, 3, 2, 4, 5, 1};
// 实现降序排序
Arrays.sort(data, new Comparator<Integer>() {
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
System.out.println(Arrays.toString(data)); //[12, 5, 4, 3, 2, 2, 1]
}
}
这里我们在调用Arrays的sort方法时,第二个参数传递的是Comparator接口的子实现类对象。所以Comparator充当的是抽象策略角色,而具体的子实现类充当的是具体策略角色。环境角色类(Arrays)应该持有抽象策略的引用来调用。那么,Arrays类的sort方法到底有没有使用Comparator子实现类中的 compare()
方法吗?让我们继续查看TimSort类的 sort()
方法,代码如下:
class TimSort<T> {
static <T> void sort(T[] a, int lo, int hi, Comparator<? super T> c,
T[] work, int workBase, int workLen) {
assert c != null && a != null && lo >= 0 && lo <= hi && hi <= a.length;
int nRemaining = hi - lo;
if (nRemaining < 2)
return; // Arrays of size 0 and 1 are always sorted
// If array is small, do a "mini-TimSort" with no merges
if (nRemaining < MIN_MERGE) {
int initRunLen = countRunAndMakeAscending(a, lo, hi, c);
binarySort(a, lo, hi, lo + initRunLen, c);
return;
}
...
}
private static <T> int countRunAndMakeAscending(T[] a, int lo, int hi,Comparator<? super T> c) {
assert lo < hi;
int runHi = lo + 1;
if (runHi == hi)
return 1;
// Find end of run, and reverse range if descending
if (c.compare(a[runHi++], a[lo]) < 0) { // Descending
while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) < 0)
runHi++;
reverseRange(a, lo, runHi);
} else { // Ascending
while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) >= 0)
runHi++;
}
return runHi - lo;
}
}
上面的代码中最终会跑到 countRunAndMakeAscending()
这个方法中。我们可以看见,只用了compare方法,所以在调用Arrays.sort方法只传具体compare重写方法的类对象就行,这也是Comparator接口中必须要子类实现的一个方法。
责任链(Chain of Responsibility)模式的定义:为了避免请求发送者与多个请求处理者耦合在一起,于是将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。
注意:责任链模式也叫职责链模式。
在责任链模式中,客户只需要将请求发送到责任链上即可,无须关心请求的处理细节和请求的传递过程,请求会自动进行传递。所以责任链将请求的发送者和请求的处理者解耦了。
责任链模式是一种对象行为型模式,其主要优点如下。
其主要缺点如下。
开发一个请假流程控制系统。请假一天以下的假只需要小组长同意即可;请假1天到3天的假还需要部门经理同意;请求3天到7天还需要总经理同意才行
关系类图
LeaveRequest
package com.zhuang.responsibility;
/**
* @Classname LeaveRequest
* @Description 请假条
* @Date 2021/3/31 16:21
* @Created by dell
*/
public class LeaveRequest {
//姓名
private String name;
// 请假天数
private int num;
// 请假内容
private String content;
public LeaveRequest(String name, int num, String content) {
this.name = name;
this.num = num;
this.content = content;
}
public String getName() {
return name;
}
public int getNum() {
return num;
}
public String getContent() {
return content;
}
}
Handler
package com.zhuang.responsibility;
/**
* @Classname Handler
* @Description 用一句话描述类的作用
* @Date 2021/3/31 16:23
* @Created by dell
*/
public abstract class Handler {
protected final static int NUM_ONE = 1;
protected final static int NUM_THREE = 3;
protected final static int NUM_SEVEN = 7;
//该领导处理的请假天数区间
private int numStart;
private int numEnd;
//领导上还有领导
private Handler nextHandler;
//设置请假天数范围
public Handler(int numStart) {
this.numStart = numStart;
}
//设置请假天数范围
public Handler(int numStart, int numEnd) {
this.numStart = numStart;
this.numEnd = numEnd;
}
//设置上级领导
public void setNextHandler(Handler nextHandler) {
this.nextHandler = nextHandler;
}
//提交请假条
public final void submit(LeaveRequest leaveRequest) {
if (this.numStart == 0) {
return;
}
//请假天数达到领导处理要求
if (leaveRequest.getNum() >= this.numStart) {
this.handleLeave(leaveRequest);
//如果还有上级 并且请假天数超过当前领导的处理范围
if (this.nextHandler != null && leaveRequest.getNum() > numEnd) {
//继续提交
this.nextHandler.submit(leaveRequest);
} else {
System.out.println("流程结束!!!");
}
}
}
//各级领导处理请假条方法
protected abstract void handleLeave(LeaveRequest leave);
}
GroupLeader
package com.zhuang.responsibility;
/**
* @Classname GroupLeader
* @Description 小组长类
* @Date 2021/3/31 16:33
* @Created by dell
*/
public class GroupLeader extends Handler {
//1-3天的假
public GroupLeader() {
super(Handler.NUM_ONE, Handler.NUM_THREE);
}
@Override
protected void handleLeave(LeaveRequest leave) {
System.out.println(leave.getName() + "请假" + leave.getNum() + "天," + leave.getContent() + "!");
System.out.println("小组长审批通过:同意!");
}
}
Manager
package com.zhuang.responsibility;
/**
* @Classname Manager
* @Description 部门经理类
* @Date 2021/3/31 16:36
* @Created by dell
*/
public class Manager extends Handler {
//3-7天的假
public Manager() {
super(Handler.NUM_THREE, Handler.NUM_SEVEN);
}
@Override
protected void handleLeave(LeaveRequest leave) {
System.out.println(leave.getName() + "请假" + leave.getNum() + "天," + leave.getContent() + "!");
System.out.println("部门经理审批通过:同意!");
}
}
GeneralManager
package com.zhuang.responsibility;
/**
* @Classname GeneralManager
* @Description 总经理类
* @Date 2021/3/31 16:38
* @Created by dell
*/
public class GeneralManager extends Handler{
//7天以上的假
public GeneralManager() {
super(Handler.NUM_THREE, Handler.NUM_SEVEN);
}
@Override
protected void handleLeave(LeaveRequest leave) {
System.out.println(leave.getName() + "请假" + leave.getNum() + "天," + leave.getContent() + "!");
System.out.println("总经理审批通过:同意!");
}
}
Client
package com.zhuang.responsibility;
/**
* @Classname Client
* @Description 责任链模式 测试类
* @Date 2021/3/31 16:39
* @Created by dell
*/
public class Client {
public static void main(String[] args) {
//请假条
LeaveRequest leave = new LeaveRequest("小庄", 3, "出去旅游");
//各位领导
Manager manager = new Manager();
GroupLeader groupLeader = new GroupLeader();
GeneralManager generalManager = new GeneralManager();
/*
小组长上司是经理 经理上司是总经理
*/
groupLeader.setNextHandler(manager);
manager.setNextHandler(generalManager);
//提交
groupLeader.submit(leave);
}
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。