java 设计模式
设计模式概述
目的
- 代码重用性 (即:相同功能的代码,不用多次编写)
- 可读性 (即:编程规范性, 便于其他程序员的阅读和理解)
- 可扩展性 (即:当需要增加新的功能时,非常的方便,称为可维护)
- 可靠性 (即:当我们增加新的功能后,对原来的功能没有影响)
- 使程序呈现高内聚,低耦合的特性(模块内部是非常紧密的,但是模块之间依赖性很低不会互相影响)
分类
设计模式分为三种类型,共23种 。
- 创建型模式:单例模式、抽象工厂模式、原型模式、建造者模式、工厂模式。
- 结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。
- 行为型模式:模版方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式(Interpreter模式)、状态模式、策略模式、职责链模式(责任链模式)。
UML类图
UML图分类:
- 用例图(use case)
- 静态结构图:类图、对象图、包图、组件图、部署图
- 动态行为图:交互图(时序图与协作图)、状态图、活动图
类之间的关系:依赖、泛化(继承)、实现、关联、聚合与组合
类图—依赖关系
代码
public class PersonServiceBean {
private PersonDao personDao;//类
public void save(Person person){}
public IDCard getIDCard(Integer personid){}
public void modify(){
Department department = new Department();
}
}
public class PersonDao{}
public class IDCard{}
public class Person{}
public class Department{}
UML
UML
哪几种是依赖:
- 类中用到了对方
- 如果是类的成员属性
- 如果是方法的返回类型
- 是方法接收的参数类型
- 方法中使用到
类图—泛化关系
泛化关系实际上就是继承关系,他是依赖关系的特例
代码
public abstract class DaoSupport{
public void save(Object entity){
}
public void delete(Object id){
}
}
public class PersonServiceBean extends Daosupport{
}
UML
类图—实现关系
实现关系实际上就是A类实现B接口,他是依赖关系的特例
代码
public interface PersonService {
public void delete(Interger id);
}
public class PersonServiceBean implements PersonService {
public void delete(Interger id){}
}
UML
类图—关联关系
类图—聚合关系
- 聚合关系(Aggregation)表示的是整体和部分的关系,整体与部分可以分开(不要部分的类没有影响就是聚合,有影响就是组合。例如:鼠标可以和电脑分离就是聚合)。
- 聚合关系是关联关系的特例,所以他具有关联的导航性与多重性。
代码
mosue和moniter与Computer的实例消失与否无关
public class Computer {
private Mouse mouse; //鼠标可以和computer分离
private Moniter moniter;//显示器可以和Computer分离
public void setMouse(Mouse mouse) {
this.mouse = mouse;
}
public void setMoniter(Moniter moniter) {
this.moniter = moniter;
}
}
class Moniter{
}
class Mouse{
}
UML
聚合与组合
此时computer实例创建 Mouse和Moniter实例就创建了,computer实例被销毁了,Mouse和Moniter实例就销毁了
类图—组合关系
- 组合关系:也是整体与部分的关系,但是整体与部分不可以分开。
- 再看一个案例:在程序中我们定义实体:Person与IDCard、Head, 那么 Head(人的头) 和 Person 就是 组合,IDCard 和 Person 就是聚合。
- 但是如果在程序中Person实体中定义了对IDCard进行级联删除,即删除Person时连同IDCard一起删除,那么IDCard 和 Person 就是组合了.
代码
public class Person{
private IDCard card;
private Head head = new Head();
}
public class IDCard{}
public class Head{}
UML
七大设计原则介绍
面向对象设计原则概述:
- 可维护性(Maintainability):指软件能够被理解、改正、适应及扩展的难易程度
- 可复用性(Reusability):指软件能够被重复使用的难易程度
- 面向对象设计的目标:
- 在于支持可维护性复用,一方面需要实现设计方案或者源代码的复用。
- 一方面要确保系统能够易于扩展和修改,具有良好的可维护性。
设计模式常用的七大原则有(面向对象设计原则)
- 单一职责原则
- 接口隔离原则
- 依赖倒转(倒置)原则
- 里氏替换原则
- 开闭原则
- 迪米特法则
- 合成复用原
单一职责原则
-
定义:
- 对类来说的,即一个类应该只负责一项职责。如类A负责两个不同职责:职责1,职责2。 当职责1需求变更而改变A时,可能造成职责2执行错误,所以需要将类A的粒度分解为 A1,A2。
- 单一职责原则是最简单的面向对象设计原则,用于控制类的粒度大小
- 一个对象应该只包含单一的职责,并且该职责被完整地封装在一个类中。
- 就一个类而言,应该仅有一个引起它变化的原因。
-
单一职责原则分析:
- 一个类(大到模块,小到方法)承担的职责越多,它被复用的可能性就越小
- 当一个职责变化时,可能会影响其他职责的运作
- 将这些职责进行分离,将不同的职责封装在不同的类中
- 将不同的变化原因封装在不同的类中
- 单一职责原则是实现高内聚、低耦合的指导方针
课堂实例
实例说明
实例解析:
错误案例与纠正
- 错误案例展示
public class SingleRsponsibility1 {
public static void main(String[] args) {
Vehicle vehicle = new Vehicle();
vehicle.move("汽车");
vehicle.move("飞机");
vehicle.move("摩托车");
}
}
//交通工具类
class Vehicle {
//违反单一职责原则,一个类管多个载具运行方式
//使得三种载具都在公路上移动
public void move(String vehicle) {
System.out.println(vehicle + "在公路上移动。。。。");
}
}
- 正确代码展示
成本高,稳定安全
public class SingleRsponsibility2 {
public static void main(String[] args) {
//天空
AirVehicle airvehicle = new AirVehicle();
airvehicle.move("飞机");
//陆地
RoadVehicle roadVehicle = new RoadVehicle();
roadVehicle.move("汽车");
roadVehicle.move("摩托车");
}
}
//改动较大,成本较高,将类分解为海陆空三个类,且需要修改客户端(在客户端创建airvehicle对象),
//但是减低变更引起的风险例如我现在要改AirVehicle类不会影响到其他类
class AirVehicle {
public void move(String vehicle) {
System.out.println(vehicle + "在天空上移动。。。。");
}
}
class RoadVehicle {
public void move(String vehicle) {
System.out.println(vehicle + "在地面上移动。。。。");
}
}
class waterVehicle {
public void move(String vehicle) {
System.out.println(vehicle + "在海洋中移动。。。。");
}
}
- 半对半错代码展示
成本更低,方法层面遵守了单一职责
public class SingleRsponsibility3 {
public static void main(String[] args) {
VehicleAll vehicleAll = new VehicleAll();
vehicleAll.moveroad("汽车");
vehicleAll.moveair("飞机");
vehicleAll.movewater("潜艇");
}
}
class VehicleAll {
//没有对原来类大的修改,只是增加方法
//在类的层面没有遵守单一职责原则,在方法的层面仍然遵守了单一职责原则
//类似案例2减低变更引起的风险 例如我现在要改moveroad方法不会影响到其他方法
public void moveroad(String vehicle) {
System.out.println(vehicle + "在公路上移动。。。。");
}
public void movewater(String vehicle) {
System.out.println(vehicle + "在海洋中移动。。。。");
}
public void moveair(String vehicle) {
System.out.println(vehicle + "在天空中移动。。。。");
}
}
-
单一职责原则注意事项和细节:
- 降低类的复杂度,一个类只负责一项职责。
- 提高类的可读性,可维护性
- 降低变更引起的风险
- 通常情况下,我们应当遵守单一职责原则,只有逻辑足够简单,才可以在代码级违 反单一职责原则;只有类中方法数量足够少,可以在方法级别保持单一职责原则。
接口隔离原则
-
定义
客户端不应该依赖它不需要的接 口,即一个类对另一个类的依赖应该建立在最小的接口上
-
下图不符合接口隔离原则
类A通过接口Interface1依赖类B,类C通过 接口Interface1依赖类D,如果接口 Interface1对于类A和类C来说不是最小接口, 那么类B和类D必须去实现他们不需要的方法。
错误案例
按上错误的图编写的代码:
package interfaceSegregationPrinciple;
public class Segregation {
public static void main(String[] args) {
// TODO Auto-generated method stub
}
}
interface Interface1{
void operation1();
void operation2();
void operation3();
void operation4();
void operation5();
}
class B implements Interface1{
@Override
public void operation1() {
// TODO Auto-generated method stub
System.out.println("B实现了operation1");
}
@Override
public void operation2() {
// TODO Auto-generated method stub
System.out.println("B实现了operation2");
}
@Override
public void operation3() {
// TODO Auto-generated method stub
System.out.println("B实现了operation3");
}
@Override
public void operation4() {
// TODO Auto-generated method stub
System.out.println("B实现了operation4");
}
@Override
public void operation5() {
// TODO Auto-generated method stub
System.out.println("B实现了operation5");
}
}
class D implements Interface1{
@Override
public void operation1() {
// TODO Auto-generated method stub
System.out.println("D实现了operation1");
}
@Override
public void operation2() {
// TODO Auto-generated method stub
System.out.println("D实现了operation2");
}
@Override
public void operation3() {
// TODO Auto-generated method stub
System.out.println("D实现了operation3");
}
@Override
public void operation4() {
// TODO Auto-generated method stub
System.out.println("D实现了operation4");
}
@Override
public void operation5() {
// TODO Auto-generated method stub
System.out.println("D实现了operation5");
}
}
class A{
//A通过接口使用B 只使用123三个方法 B类中的45白写
public void depend1(Interface1 i) {
i.operation1();
}
public void depend2(Interface1 i) {
i.operation2();
}
public void depend3(Interface1 i) {
i.operation3();
}
}
class C{
//C通过接口使用D 只使用145三个方法 D中23白写
public void depend1(Interface1 i) {
i.operation1();
}
public void depend4(Interface1 i) {
i.operation4();
}
public void depend5(Interface1 i) {
i.operation5();
}
}
纠正上面错误案例
- 按隔离原则应当这样处理:
将接口Interface1拆分为独立的几个接口, 类A和类C分别与他们需要的接口建立依赖 关系。也就是采用接口隔离原则。
接口隔离UML图
按上正确的图编写代码
package sevenprinciple.interfaceSegregationPrinciple;
public class Segregation1 {
public static void main(String[] args) {
// TODO Auto-generated method stub
A1 a = new A1();
a.depend1(new B1());//A通过接口依赖B
a.depend2(new B1());
a.depend3(new B1());
System.out.println();
C1 c1 = new C1();
c1.depend1(new D1());//C通过接口依赖D
c1.depend4(new D1());
c1.depend5(new D1());
}
}
interface Interface1{
void operation1();
}
interface Interface2{
void operation2();
void operation3();
}
interface Interface3{
void operation4();
void operation5();
}
class B1 implements Interface1,Interface2{
@Override
public void operation1() {
// TODO Auto-generated method stub
System.out.println("B实现了operation1");
}
@Override
public void operation2() {
// TODO Auto-generated method stub
System.out.println("B实现了operation2");
}
@Override
public void operation3() {
// TODO Auto-generated method stub
System.out.println("B实现了operation3");
}
}
class D1 implements Interface1,Interface3{
@Override
public void operation1() {
// TODO Auto-generated method stub
System.out.println("D实现了operation1");
}
@Override
public void operation4() {
// TODO Auto-generated method stub
System.out.println("D实现了operation4");
}
@Override
public void operation5() {
// TODO Auto-generated method stub
System.out.println("D实现了operation5");
}
}
class A1{
//A通过Interface1,Interface2使用B
public void depend1(Interface1 i) {
i.operation1();
}
public void depend2(Interface2 i) {
i.operation2();
}
public void depend3(Interface2 i) {
i.operation3();
}
}
class C1{
//A通过Interface1,Interface3使用B
public void depend1(Interface1 i) {
i.operation1();
}
public void depend4(Interface3 i) {
i.operation4();
}
public void depend5(Interface3 i) {
i.operation5();
}
}
课堂案例
解决方法:
将上图中的大接口分为很多小接口
依赖倒转原则
定义:
- 高层模块(高层一般是指抽象类,接口))不应该依赖低层模块,二者都应该依赖其抽象(抽象类,接口)
- 抽象不应该依赖细节,细节应该依赖抽象
- 依赖倒转(倒置)的中心思想是面向接口编程。
依赖倒转原则的设计理念
-
相对于细节的多变性,抽象的东西要稳定的 多。以抽象为基础搭建的架构比以细节为基础的架构要稳定的多。在java中,抽象指的是接口或抽象类,细节就是具体的实现类。
-
使用接口或抽象类的价值在于设计,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成
错误实例
package sevenprinciple.DependenceInversion;
public class DependenceInversion {
public static void main(String[] args) {
new Person().receive(new Email());
}
}
class Email{
public String msg() {
return "信息:hello";
}
}
//完成Person 接收消息 的功能
class Person{
//receive(Email email)中Email是个类意味着 Person依赖Email类
//分析:
//1.简单
//若获取对象是微信,短信,则需要新增类,同时person类也需要增加相应的接收方法
//3.解决思路:引入一个抽象的接口IReceiver,表示接收者,Person类与接口路发生依赖
public void receive(Email email){
System.out.println(email.msg());
}
}
分析:
-
上文代码中receive(Email email)中Email是个类意味着 Person依赖Email类
-
上文代码的设计比较简单
-
但是若获取对象是微信,短信,则需要新增类,同时person类也需要增加相应的接收方法
解决思路:引入一个抽象的接口IReceiver,表示接收者,Person类与接口路发生依赖
按照解决思路修改代码
package sevenprinciple.DependenceInversion;
public class DependenceInversionImprove {
public static void main(String[] args) {
new Person1().receive(new Email1());
new Person1().receive(new Wechat());
}
}
class Email1 implements IReveiver{
public String msg() {
return "信息:hello";
}
}
class Wechat implements IReveiver{
public String msg() {
return "信息:hello";
}
}
//完成Person 接收消息 的功能
class Person1{
//此时改变接收对象比如不是Email是wechat,person类中不需要发生改变
//此时person依赖IReceiver接口
public void receive(IReveiver iReveiver){
System.out.println(iReveiver.msg());
}
}
interface IReveiver{
public String msg();
}
分析:
- 创建一个接口类IReveiver
interface IReveiver{
public String msg();
}
- 使要接受信息的类实现接口IReveiver,并重写它的msg()方法
class Email1 implements IReveiver{
public String msg() {
return "信息:hello";
}
}
class Wechat implements IReveiver{
public String msg() {
return "信息:hello";
}
}
- 使person类依赖接口IReceiver
class Person1{
//此时改变接收对象比如不是Email是wechat,person类中不需要发生改变
//此时person依赖IReceiver接口
public void receive(IReveiver iReveiver){
System.out.println(iReveiver.msg());
}
}
依赖关系传递的三种方式
- 接口传递
interface IOpenAndClose {
public void open(ITV tv);//抽象方法,接收接口
}
//实现接口
class OpenAndClose implements IOpenAndClose {
public void open(ITV tv) {
tv.play();
}
}
class ChangHong implements ITV {
@Override
public void play() {
// TODO Auto-generated method stub
System.out.println("长虹电视机,打开");
}
}
interface ITV { //ITV接口
public void play();
}
class go{
public static void main(String[] args) {
OpenAndClose openAndClose = new OpenAndClose();
openAndClose.open(changHong);
}
}
- 通过构造器实现依赖传递
interface IOpenAndClose {
public void open(); // 抽象方法
}
interface ITV { // ITV接口
public void play();
}
class ChangHong implements ITV {
@Override
public void play() {
// TODO Auto-generated method stub
System.out.println("长虹电视机,打开");
}
}
class OpenAndClose implements IOpenAndClose {
public ITV tv; // 成员
public OpenAndClose(ITV tv) { // 构造器
this.tv = tv;
}
public void open() {
this.tv.play();
}
}
class go{
public static void main(String[] args) {
OpenAndClose openAndClose = new OpenAndClose();
OpenAndClose openAndClose = new OpenAndClose(changHong);
openAndClose.open();
}
}
- setter方式传递
interface IOpenAndClose {
public void open(); // 抽象方法
public void setTv(ITV tv);
}
interface ITV { // ITV接口
public void play();
}
class OpenAndClose implements IOpenAndClose {
private ITV tv;
public void setTv(ITV tv) {
this.tv = tv;
}
public void open() {
this.tv.play();
}
}
class ChangHong implements ITV {
@Override
public void play() {
// TODO Auto-generated method stub
System.out.println("长虹电视机,打开");
}
}
class go{
public static void main(String[] args) {
OpenAndClose openAndClose = new OpenAndClose();
OpenAndClose openAndClose = new OpenAndClose();
openAndClose.setTv(changHong);
openAndClose.open();
}
}
课堂实例
解决:
-
TXTDataConvertor和ExcelDataConvertor继承一个DataConvertor抽象类
-
CustomerDAO依赖DataConvertor抽象类和config.xml(配置文件)
-
此时数据类的转换只需改变配置文件,例如
-
改为ExcelDataConvertor,只需改为
<class Name>ExcelDataConvertor</className>
-
增加一个LoLDataConvertor,使LoLDataConvertor继承DataConvertor抽象类,再将配置文件改为
<class Name>LoLDataConvertor</className>
-
里氏替换原则
java中的继承性的思考和说明:
-
继承包含这样一层含义:父类中凡是已经实现好的方法,实际上是在设定规范和契约,虽然它不强制要求所有的子类必须遵循这些契约,但是如果子类对这些已经实现的方法任意修改,就会对整个继承体系造成破坏。
-
继承在给程序设计带来便利的同时,也带来了弊端。比如使用继承会给程序带来侵 入性,程序的可移植性降低,增加对象间的耦合性,例如:如果一个类被其他的类所继承, 则当这个类需要修改时,必须考虑到所有的子类,并且父类修改后,所有涉及到子类的功能都有可能产生故障
因此提出来里氏替换原则来解决上面问题
基本介绍
-
如果对每个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序 P在所有的对象o1都代换成o2时,程序P的行为没有发生变化,那么类型T2是类型T1 的子类型。换句话说,所有引用基类的地方必须能透明地使用其子类的对象(也就是说父类所拥有的功能子类不能改变)。
-
在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类的方法(若是子父类都不一样如何透明的使用其子类的对象)
-
里氏替换原则告诉我们,继承实际上让两个类耦合性增强了(模块的联系紧密,不符合低耦合),在适当的情况下,可以通过聚合,组合,依赖 来解决问题(去降低耦合性)。
错误案例:
package sevenprinciple.LiskovSubstitutionPrinciple;
public class Liskov {
public static void main(String[] args) {
// TODO Auto-generated method stub
A a = new A();
System.out.println("11-3=" + a.func1(11, 3));
System.out.println("1-8=" + a.func1(1, 8));
System.out.println("-----------------------");
B b = new B();
//父类中的方法被重写 此时调用b.func1()不是减法功能 会发出错误
System.out.println("11-3=" + b.func1(11, 3));
System.out.println("1-8=" + b.func1(1, 8));
System.out.println("11+3+9=" + b.func2(11, 3));
}
}
class A {
public int func1(int num1, int num2) {
return num1 - num2;
}
}
// B继承了A
// 增加了 一个新的功能 完成两数相加再加9
class B extends A {
//无意间重写了父类的方法
public int func1(int a, int b) {
return a + b;
}
public int func2(int a, int b) {
return func1(a, b) + 9;
}
}
结果:
错误原因与解决:
- 我们发现原来运行正常的相减功能发生了错误。原因就是类B无意中重写了父类的 方法,造成原有功能出现错误。在实际编程中,我们常常会通过重写父类的方法完 成新的功能,这样写起来虽然简单,但整个继承体系的复用性会比较差。特别是运行多态比较频繁的时候
- 通用的做法是:原来的父类和子类都继承一个更通俗的基类,原有的继承关系去掉, 采用依赖,聚合,组合等关系代替
解决后代码
package sevenprinciple.LiskovSubstitutionPrinciple;
public class LiskovImprove {
public static void main(String[] args) {
// TODO Auto-generated method stub
A2 a = new A2();
System.out.println("11-3=" + a.func1(11, 3));
System.out.println("1-8=" + a.func1(1, 8));
System.out.println("-----------------------");
B2 b = new B2();
//因为B类不载继承A类 所以不会发生无意间重写父类导致错误
System.out.println("11-3=" + b.func3(11, 3));
System.out.println("1-8=" + b.func3(1, 8));
System.out.println("11+3+9=" + b.func2(11, 3));
}
}
class A2 extends Base{
public int func1(int num1, int num2) {
return num1 - num2;
}
}
// B继承了Base
// 增加了 一个新的功能 完成两数相加再加9
class B2 extends Base {
//如果B要使用A的方法,使用组合关系
private A2 a=new A2();
//我们仍然想使用A的方法
public int func3(int a,int b) {
return this.a.func1(a, b);
}
public int func1(int a, int b) {
return a + b;
}
public int func2(int a, int b) {
return func1(a, b) + 9;
}
}
//创建一个更加基础的基类
class Base{
//把更加基础的方法和成员写到Base类
}
开闭原则
基本介绍:
-
开闭原则(Open Closed Principle)是编程中最基础、最重要的设计原则
-
一个软件实体如类,模块和函数应该对扩展开放(对提供方),对修改关闭(原先使用的)。用抽象构建框架,用实现扩展细节。
-
当软件需要变化时,尽量通过扩展软件实体的行为来实现变化(例如新建一个类而不是修改原来的类),而不是通过修改已有的代码来实现变化。
-
编程中遵循其它原则,以及使用设计模式的目的就是遵循开闭原则。
-
在开闭原则的定义中,软件实体可以是一个软件模块、一个由多个类组成的局部结构或一个独立的类。开闭原则是指软件实体应尽量在不修改原有代码的情况下进行扩展。
开闭原则分析 :
- 抽象化是开闭原则的关键
- 相对稳定的抽象层 + 灵活的具体层
- 对可变性封装原则(Principle of Encapsulation of Variation, EVP):找到系统的可变因素并将其封装起来
实例讲解
违反Ocp的错误案例
UML类图
代码
package sevenprinciple.OpenClosedPrinciple;
public class Ocp {
public static void main(String[] args) {
GraphicEditor editor=new GraphicEditor();
editor.drawShape(new Circle()); // new的时候构造器会使用
editor.drawShape(new Rectangle());
}
}
class GraphicEditor {
//依赖Shape
public void drawShape(Shape s) {
if (s.m_type == 1)
drawRectangle(s);
else if (s.m_type == 2)
drawCircle(s);
}
//依赖Shape的子类Rectangle
public void drawRectangle(Shape r) {
System.out.println("绘制矩形");
}
//依赖Shape的子类Circle
public void drawCircle(Shape r) {
System.out.println("绘制圆形");
}
}
class Shape {
int m_type;
}
class Rectangle extends Shape {
Rectangle() {
super.m_type = 1;
}
}
class Circle extends Shape {
Circle() {
super.m_type = 2;
}
}
此案例的优缺点:
- 优点是比较好理解,简单易操作。
- 缺点是违反了设计模式的ocp原则,即对扩展开放(提供方),对修改关闭(使用方)。 即当我们给类增加新功能的时候,尽量不修改代码,或者尽可能少修改代码.
- 比如我们这时要新增加一个图形种类三角形,我们需要做如下修改,修改的地方较多,代码如下
使用方
//[使用方]
class GraphicEditor {
//依赖Shape
public void drawShape(Shape s) {
if (s.m_type == 1)
drawRectangle(s);
else if (s.m_type == 2)
drawCircle(s);
//此处需要对使用方修改!!!!!!!!!!!!!!!!!!!!!!!!!!!
else if(s.m_type == 3)
drawTriangle(s);
}
//依赖Shape的子类Rectangle
public void drawRectangle(Shape r) {
System.out.println("绘制矩形");
}
//依赖Shape的子类Circle
public void drawCircle(Shape r) {
System.out.println("绘制圆形");
}
//此处需要对使用方修改!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
public void drawTriangle(Shape r) {
System.out.println("绘制三角形");
}
}
可看到使用方需发生两次修改
提供方
//新建一个三角类
class Triangle extends Shape{
public Triangle() {
super.m_type = 3;
}
}
对上案例进行遵守Ocp的改进
思路:
把创建Shape类做成抽象类,并提供一个抽象的draw方法,让子类去实现即可, 这样我们有新的图形种类时,只需要让新的图形类继承Shape,并实现draw方法即可, 使用方的代码就不需要修改,满足了开闭原则
代码
package sevenprinciple.OpenClosedPrinciple;
public class OcpImprove {
public static void main(String[] args) {
GraphicEditor1 editor=new GraphicEditor1();
editor.drawShape(new Circle()); // new的时候构造器会使用
editor.drawShape(new Rectangle());
editor.drawShape(new Triangle());
editor.drawShape(new Other());
}
}
//[使用方]完全不用修改 ***************对修改关闭***********
class GraphicEditor1{
//依赖Shape
public void drawShape(Shape s) {
s.draw();
}
}
abstract class Shape {
int m_type;
public abstract void draw();
}
class Rectangle extends Shape {
@Override
public void draw() {
// TODO Auto-generated method stub
System.out.println("绘制矩形");
}
}
class Circle extends Shape {
@Override
public void draw() {
// TODO Auto-generated method stub
System.out.println("绘制圆形");
}
}
class Triangle extends Shape{
@Override
public void draw() {
// TODO Auto-generated method stub
System.out.println("绘制三角形");
}
}
//新建一个其他图形******* 对扩展开放************
class Other extends Shape{
@Override
public void draw() {
// TODO Auto-generated method stub
System.out.println("绘制其他图形");
}
}
迪米特法则
基本介绍
-
一个对象应该对其他对象保持最少的了解
-
类与类关系越密切,耦合度越大
-
迪米特法则(Demeter Principle)又叫最少知道原则,即一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外除了提供的public方法,不对外泄露任何信息
-
迪米特法则还有个更简单的定义:只与直接的朋友通信 。
直接的朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系, 我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖,关联,组合,聚合等。其中,我们称出现成员变量,方法参数,方法返回值中的类为直接的朋友
成员变量——直接朋友
public class Test { A a; } class A{ }
方法参数——直接朋友
public class Test { public void Hello(A a) { } } class A{ }
方法返回值——直接朋友
public class Test { public A Hello() { return new A(); } } class A{ }
-
而出现在局部变量中的类不是直接的朋友。也就是说,陌生的类最好不要以局部变量的形式出现在类的内部。
public class Test {
public void Hello() {
A a=new A();
}
}
class A{
}
错误案例
有一个学校,下属有各个学院和 总部,现要求打印出学校总部员 工ID和学院员工的id
完整代码
package sevenprinciple.DemeterPrinciple;
import java.util.ArrayList;
import java.util.List;
//客户端
public class Demeter1 {
public static void main(String[] args) {
//创建了一个 SchoolManager 对象
SchoolManager schoolManager = new SchoolManager();
//输出学院的员工id 和 学校总部的员工信息
schoolManager.printAllEmployee(new CollegeManager());
}
}
//学校总部员工类
class Employee {
private String id;
public void setId(String id) {
this.id = id;
}
public String getId() {
return id;
}
}
//学院的员工类
class CollegeEmployee {
private String id;
public void setId(String id) {
this.id = id;
}
public String getId() {
return id;
}
}
//管理学院员工的管理类
class CollegeManager {
//返回学院的所有员工
public List<CollegeEmployee> getAllEmployee() {
List<CollegeEmployee> list = new ArrayList<CollegeEmployee>();
for (int i = 0; i < 10; i++) { //这里我们增加了10个员工到 list
CollegeEmployee emp = new CollegeEmployee();
emp.setId("学院员工id= " + i);
list.add(emp);
}
return list;
}
}
//学校管理类
//分析 SchoolManager 类的直接朋友类有哪些 Employee、CollegeManager
//CollegeEmployee 不是 直接朋友 而是一个陌生类,这样违背了 迪米特法则
class SchoolManager {
//返回学校总部的员工
public List<Employee> getAllEmployee() {
List<Employee> list = new ArrayList<Employee>();
for (int i = 0; i < 5; i++) { //这里我们增加了5个员工到 list
Employee emp = new Employee();
emp.setId("学校总部员工id= " + i);
list.add(emp);
}
return list;
}
//该方法完成输出学校总部和学院员工信息(id)
void printAllEmployee(CollegeManager sub) {
//分析问题
//1. 这里的 CollegeEmployee 不是 SchoolManager的直接朋友
//2. CollegeEmployee 是以局部变量方式出现在 SchoolManager
//3. 违反了 迪米特法则
//获取到学院员工
List<CollegeEmployee> list1 = sub.getAllEmployee();
System.out.println("------------学院员工------------");
for (CollegeEmployee e : list1) {
System.out.println(e.getId());
}
//获取到学校总部员工
List<Employee> list2 = this.getAllEmployee();
System.out.println("------------学校总部员工------------");
for (Employee e : list2) {
System.out.println(e.getId());
}
}
}
对以上代码的SchoolManager类进行分析,找到直接朋友与陌生人
-
返回是是Employee类型 所以Employee为SchoolManager类的直接朋友
public List getAllEmployee() {
List list = new ArrayList();for (int i = 0; i < 5; i++) { //这里我们增加了5个员工到 list Employee emp = new Employee(); emp.setId("学校总部员工id= " + i); list.add(emp); } return list;
}
-
方法的形参是CollegeManager类所以CollegeManager类是SchoolManager的直接朋友;
List list1 = sub.getAllEmployee(); list1是局部变量所以CollegeEmployee不是SchoolManager类的直接朋友。
void printAllEmployee(CollegeManager sub) {
List<CollegeEmployee> list1 = sub.getAllEmployee(); System.out.println("------------学院员工------------"); for (CollegeEmployee e : list1) { System.out.println(e.getId()); } //获取到学校总部员工 List<Employee> list2 = this.getAllEmployee(); System.out.println("------------学校总部员工------------"); for (Employee e : list2) { System.out.println(e.getId()); }
}
分析问题
-
这里的 CollegeEmployee类不是 SchoolManager类的直接朋友
-
CollegeEmployee 是以局部变量方式出现在 SchoolManager
-
违反了 迪米特法则
纠正错误
解决方法:
CollegeEmployee类是CollegeManager类的直接朋友,所以可以将输出学院的员工方法,封装到CollegeManager。
修改后的CollegeManager类为:
class CollegeManager {
//返回学院的所有员工
public List<CollegeEmployee> getAllEmployee() {
List<CollegeEmployee> list = new ArrayList<CollegeEmployee>();
for (int i = 0; i < 10; i++) { //这里我们增加了10个员工到 list
CollegeEmployee emp = new CollegeEmployee();
emp.setId("学院员工id= " + i);
list.add(emp);
}
return list;
}
//输出学院员工的信息
public void printEmployee() {
//获取到学院员工
List<CollegeEmployee> list1 = getAllEmployee();
System.out.println("------------学院员工------------");
for (CollegeEmployee e : list1) {
System.out.println(e.getId());
}
}
}
修改后的完整代码
package sevenprinciple.DemeterPrinciple.improve;
import java.util.ArrayList;
import java.util.List;
//客户端
public class Demeter1 {
public static void main(String[] args) {
System.out.println("~~~使用迪米特法则的改进~~~");
//创建了一个 SchoolManager 对象
SchoolManager schoolManager = new SchoolManager();
//输出学院的员工id 和 学校总部的员工信息
schoolManager.printAllEmployee(new CollegeManager());
}
}
//学校总部员工类
class Employee {
private String id;
public void setId(String id) {
this.id = id;
}
public String getId() {
return id;
}
}
//学院的员工类
class CollegeEmployee {
private String id;
public void setId(String id) {
this.id = id;
}
public String getId() {
return id;
}
}
//管理学院员工的管理类
class CollegeManager {
//返回学院的所有员工
public List<CollegeEmployee> getAllEmployee() {
List<CollegeEmployee> list = new ArrayList<CollegeEmployee>();
for (int i = 0; i < 10; i++) { //这里我们增加了10个员工到 list
CollegeEmployee emp = new CollegeEmployee();
emp.setId("学院员工id= " + i);
list.add(emp);
}
return list;
}
//输出学院员工的信息
public void printEmployee() {
//获取到学院员工
List<CollegeEmployee> list1 = getAllEmployee();
System.out.println("------------学院员工------------");
for (CollegeEmployee e : list1) {
System.out.println(e.getId());
}
}
}
//学校管理类
//分析 SchoolManager 类的直接朋友类有哪些 Employee、CollegeManager
//CollegeEmployee 不是 直接朋友 而是一个陌生类,这样违背了 迪米特法则
class SchoolManager {
//返回学校总部的员工
public List<Employee> getAllEmployee() {
List<Employee> list = new ArrayList<Employee>();
for (int i = 0; i < 5; i++) { //这里我们增加了5个员工到 list
Employee emp = new Employee();
emp.setId("学校总部员工id= " + i);
list.add(emp);
}
return list;
}
//该方法完成输出学校总部和学院员工信息(id)
void printAllEmployee(CollegeManager sub) {
//分析问题
//1. 将输出学院的员工方法,封装到CollegeManager
sub.printEmployee();
//获取到学校总部员工
List<Employee> list2 = this.getAllEmployee();
System.out.println("------------学校总部员工------------");
for (Employee e : list2) {
System.out.println(e.getId());
}
}
}
课堂案例
解决:
合成复用原则
基本介绍:原则是尽量使用依赖/合成/聚合的方式,而不是使用继承
合成复用原则分析
- 合成复用原则就是在一个新的对象里通过关联关系(包括组合关系和聚合关系)来使用一些已有的对象,使之成为新对象的一部分
- 新对象通过委派调用已有对象的方法达到复用功能的目的
- 复用时要尽量使用组合/聚合关系(关联关系),少用继承
继承复用与组合/聚合复用比较
- 继承复用:
- 实现简单,易于扩展。破坏系统的封装性;
- 从基类继承而来的实现是静态的,不可能在运行时发生改变,没有足够的灵活性;
- 只能在有限的环境中使用。(“白箱”复用 )
- 组合/聚合复用:
- 耦合度相对较低,有选择性地调用成员对象的操作;
- 可以在运行时动态进行,新对象可以动态地引用与成员对象类型相同的其他对象。(“黑箱”复用 )
继承 依赖 合成 聚合
- 继承(B类通过继承使用A类的方法)
public class B extends A {
public static void main(String[] args) {
new B().method1();
}
}
class A {
public void method1() {
System.out.println("方法一");
}
public void method2() {
System.out.println("方法二");
}
}
- 依赖(B类通过依赖使用A类的方法)
public class B {
public static void main(String[] args) {
new B().method3(new A());
}
public void method3(A a) {
a.method1();
}
}
class A {
public void method1() {
System.out.println("方法一");
}
public void method2() {
System.out.println("方法二");
}
}
- 聚合(B类通过聚合使用A类的方法)
public class B {
static A a;
public static void main(String[] args) {
B b=new B();
b.setA(new A());
a.method1();
}
public void setA(A a) {
this.a = a;
}
}
class A {
public void method1() {
System.out.println("方法一");
}
public void method2() {
System.out.println("方法二");
}
}
- 组合(B类通过组合使用A类的方法)
public class B {
public static void main(String[] args) {
A a = new A();
a.method1();
}
}
class A {
public void method1() {
System.out.println("方法一");
}
public void method2() {
System.out.println("方法二");
}
}
课堂案例
解决方式:
-
CustomerDAO关联DBUtil,OracleDBUtil继承DBUtil
-
CustomerDAO关联DBUtil时,可
DBUtil util=new OracleDBUtil();
此时使用util.getConnection()使用的就是Oracle数据库
创建型模式
单例模式
定义
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)。
单例模式注意事项和细节说明 :
- 单例模式保证了 系统内存中该类只存在一个对象,节省了系统资源,对于一些需 要频繁创建销毁的对象,使用单例模式可以提高系统性能
- 当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使 用new
- 单例模式使用的场景:需要频繁的进行创建和销毁的对象、创建对象时耗时过多或 耗费资源过多(即:重量级对象),但又经常用到的对象、工具类对象、频繁访问数 据库或文件的对象(比如数据源、session工厂等)
单例模式有八种方式:
- 饿汉式(静态常量)
- 饿汉式(静态代码块)
- 懒汉式(线程不安全)
- 懒汉式(线程安全,同步方法)
- 懒汉式(线程安全,同步代码块)
- 双重检查
- 静态内部类
- 枚举
饿汉式(静态常量)
要点:
- 构造器私有化 (防止 new ,使得外部只有一个实例)
- 类的内部创建对象
- 向外暴露一个静态的公共方法。
代码:
public class SingletonTest01 {
public static void main(String[] args) {
Singleton singleton = Singleton.getSingleton();
Singleton singleton1 = Singleton.getSingleton();
System.out.println(singleton==singleton1);//true
}
}
//饿汉式(静态变量)
class Singleton {
int count=0;
// 1.构造器私有化,外部(此类之外)不能new了 new Singleton()这个()就是构造器
private Singleton() {
//SingletonTest01 get了两次 但是此处count为1,因为get只是获得不是创建,实际只创建了一次,因为只在此类new了一次
System.out.println(++count);
}
// 2.本类内部创建对象实例
private final static Singleton SINGLETON = new Singleton();
// 3.提供一个共有的静态方法返回
public static Singleton getSingleton() {
return SINGLETON;
}
}
优缺点:
-
优点:这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同 步问题。
-
缺点:在类装载的时候就完成实例化,没有达到Lazy Loading的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费
-
这种方式基于classloder机制避免了多线程的同步问题,不过,instance在类装载 时就实例化,在单例模式中大多数都是调用getInstance方法, 但是导致类装载的原因有很多种,因此不能确定有其他的方式(或者其他的静态方法)导致类 装载,这时候初始化instance就没有达到lazy loading的效果
结论:这种单例模式可用,可能造成内存浪费
饿汉式(静态代码块)
package singleton_mode.type2;
public class SingletonTest02 {
public static void main(String[] args) {
Singleton singleton = Singleton.getSingleton();
Singleton singleton1 = Singleton.getSingleton();
System.out.println(singleton==singleton1);//true
}
}
//饿汉式(静态变量)
class Singleton {
int count=0;
// 1.构造器私有化,外部(此类之外)不能new了 new Singleton()这个()就是构造器
private Singleton() {
//SingletonTest01get了两次 但是此处count为1,因为get只是获得不是创建,实际只创建了一次,因为只在此类new了一次
System.out.println(++count);
}
// 2.本类内部创建对象实例
private static Singleton SINGLETON ;
//在静态代码块中创建单例
static {
SINGLETON=new Singleton();
}
// 3.提供一个共有的静态方法返回
public static Singleton getSingleton() {
return SINGLETON;
}
}
优缺点说明:
-
这种方式和上面的方式其实类似,只不过将类实例化的过程放在了静态代码块中,也是在类装载的时候,就执行静态代码块中的代码,初始化类的实例。
-
优缺点和上面是一样的。
结论:这种单例模式可用,但是可能造成内存浪费
懒汉式(线程不安全)
代码:
package singleton_mode.type3;
public class SingletonTest03 {
//懒汉式(线程不安全)
public static void main(String[] args) {
// TODO Auto-generated method stub
Singleton singleton = Singleton.getSingleton();
Singleton singleton1 = Singleton.getSingleton();// 已经创建了一个 第二次创建就是直接返回
System.out.println(singleton == singleton1);// true
}
}
class Singleton {
private static Singleton singleton;
private Singleton() {
}
// 提供一个静态的共有方法,当使用到该方法是时才去创建
public static Singleton getSingleton() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
优缺点说明:
- 起到了Lazy Loading的效果,使用时才创建(将new放在了get…()中),但是只能在单线程下使用。
- 如果在多线程下,一个线程进入了if (singleton == null)判断语句块,还未来得及 往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。所以在多线程环境下不可使用这种方式 结论:在实际开发中,不要使用这种方式(还不如饿汉式这个可能会直接破坏单例模式出现多个对象)
懒汉式(线程安全,同步方法)
代码:
package singleton_mode.type4;
public class SingletonTest04 {
//懒汉式(线程安全)
public static void main(String[] args) {
// TODO Auto-generated method stub
Singleton singleton = Singleton.getSingleton();
Singleton singleton1 = Singleton.getSingleton();// 已经创建了一个 第二次创建就是直接返回
System.out.println(singleton == singleton1);// true
}
}
class Singleton {
private static Singleton singleton;
private Singleton() {
}
// 提供一个静态的共有方法,当使用到该方法是时才去创建
//synchronized解决线程安全问题
public static synchronized Singleton getSingleton() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
优缺点说明:
-
解决了线程不安全问题
-
效率太低了,每个线程在想获得类的实例时候,执行getInstance()方法都要进行 同步。而其实这个方法只执行一次实例化代码就够了,后面的想获得该类实例, 直接return就行了。方法进行同步效率太低
结论:在实际开发中,不推荐使用这种方式
懒汉式(线程安全,同步代码块)
优缺点说明:
-
这种方式,本意是想对第四种实现方式的改进,因为前面同步方法效率太低, 改为同步产生实例化的的代码块
-
但是这种同步并不能起到线程同步的作用。跟第3种实现方式遇到的情形一 致,假如一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行, 另一个线程也通过了这个判断语句,这时便会产生多个实例
-
两个线程同时会通过判断条件,虽然同一时间只有一个线程能够执行,但是由于另外一个线程已经通过if,所以一个线程创建完了,另一个就会创建,导致违反单例。
结论:在实际开发中,不能使用这种方式
双重检查
代码实例
public class SingletonTest05 {
public static void main(String[] args) {
Singleton singleton = Singleton.getSingleton();
Singleton singleton1 = Singleton.getSingleton();
System.out.println(singleton == singleton1);
}
}
class Singleton{
private static volatile Singleton singleton;
private Singleton() {
}
public static Singleton getSingleton() {
if(singleton==null) {
synchronized (SingletonTest05.class) {
if(singleton==null) {
singleton=new Singleton();
}
}
}
return singleton;
}
}
优缺点说明:
- Double-Check概念是多线程开发中常使用到的,如代码中所示,我们进行了两次if (singleton == null)检查,这样就可以保证线程安全了。
- 这样,实例化代码只用执行一次,后面再次访问时,判断if (singleton == null), 直接return实例化对象,也避免的反复进行方法同步.
- 线程安全;延迟加载;效率较高
结论:在实际开发中,推荐使用这种单例设计模式
静态内部类
代码实例
public class SingletonTest06 {
public static void main(String[] args) {
Singleton singleton = Singleton.getSingleton();
Singleton singleton1 = Singleton.getSingleton();
System.out.println(singleton == singleton1);
}
}
class Singleton{
private Singleton() {
}
private static class SingletonInstance{
private static final Singleton Instance=new Singleton();
}
public static Singleton getSingleton() {
return SingletonInstance.Instance;
}
}
优缺点说明:
-
这种方式采用了类装载的机制来保证初始化实例时只有一个线程。
-
静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化 时,调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的实例化。
-
类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们 保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。
-
优点:避免了线程不安全,利用静态内部类特点实现延迟加载,效率高
结论:推荐使用.
枚举
代码实例:
package singleton_mode.type7;
public class SingletonTest07 {
public static void main(String[] args) {
Singleton singleton=Singleton.Instance;
Singleton singleton2=singleton.Instance;
System.out.println(singleton==singleton2);//true
}
}
enum Singleton{
Instance;
public void sayOK() {
System.out.println("ok-");
}
}
优缺点说明:
-
这借助JDK1.5中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而 且还能防止反序列化重新创建新的对象。
-
这种方式是Effective Java作者Josh Bloch 提倡的方式
结论:推荐使用
工厂模式
简单工厂模式
基本介绍
- 简单工厂模式是属于创建型模式,是工厂模式的一种。简单工厂模式是由一 个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族 中最简单实用的模式
- 简单工厂模式:定义了一个创建对象的类,由这个类来封装实例化对象的行为(代码)
- 在软件开发中,当我们会用到大量的创建某种、某类或者某批对象时,就会使用到工厂模式.
实例讲解
-
题目:
完成一个披萨的项目:要便于披萨种类的扩展,要便于维护
- 披萨的种类很多(比如 GreekPizz、CheesePizz 等)
- 披萨的制作有 prepare,bake, cut, box
- 完成披萨店订购功能
-
使用传统方法制作项目
-
UML
-
代码展示
Pizza
//将Pizza 类做成抽象 public abstract class Pizza { protected String name; //名字 //准备原材料, 不同的披萨不一样,因此,我们做成抽象方法 public abstract void prepare(); public void bake() { System.out.println(name + " baking;"); } public void cut() { System.out.println(name + " cutting;"); } //打包 public void box() { System.out.println(name + " boxing;"); } public void setName(String name) { this.name = name; } }
GreekPizza
public class GreekPizza extends Pizza { @Override public void prepare() { // TODO Auto-generated method stub System.out.println(" 给希腊披萨 准备原材料 "); } }
OrderPizza
public class OrderPizza { public OrderPizza() { Pizza pizza = null; String orderType; // 订购披萨的类型 do { orderType = getType(); if (orderType.equals("greek")) { pizza = new GreekPizza(); pizza.setName(" 希腊披萨 "); } else if (orderType.equals("cheese")) { pizza = new CheesePizza(); pizza.setName(" 奶酪披萨 "); } else if (orderType.equals("pepper")) { pizza = new PepperPizza(); pizza.setName("胡椒披萨"); } else { break; } //输出pizza 制作过程 pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); } while (true); } // 写一个方法,可以获取客户希望订购的披萨种类 private String getType() { try { BufferedReader strin = new BufferedReader(new InputStreamReader(System.in)); System.out.println("input pizza 种类:"); String str = strin.readLine(); return str; } catch (IOException e) { e.printStackTrace(); return ""; } } }
PizzaStore
//相当于一个客户端,发出订购 public class PizzaStore { public static void main(String[] args) { new OrderPizza(); } }
-
传统的方式的优缺点
-
优点是比较好理解,简单易操作。
-
缺点是违反了设计模式的ocp原则,即对扩展开放,对修改关闭。即当我们给类增 加新功能的时候,尽量不修改代码,或者尽可能少修改代码.
比如我们这时要新增加一个Pizza的种类(Pepper披萨),我们需要做如下修改:
1.新增一个类
public class PepperPizza extends Pizza { @Override public void prepare() { // TODO Auto-generated method stub System.out.println(" 给胡椒披萨准备原材料 "); } }
2.在OrderPizza加一个else if()
else if (orderType.equals("pepper")) { pizza = new PepperPizza(); pizza.setName("胡椒披萨"); }
在实际生活中不可能只有一个店面需要原材料,所以多个客户端都得加此行代码。
-
-
-
使用简单工厂模式制作项目
-
改进的思路分析
分析:修改代码可以接受,但是如果我们在其它的地方也有创建Pizza的代码,就意味着,也需要修改,而创建Pizza的代码,往往有多处。
思路:把创建Pizza对象封装到一个类中,这样我们有新的Pizza种类时,只需要修改该类就可,其它有创建到Pizza对象的代码就不需要修改了.(这就是简单工厂模式)
-
UML
由下图可知添加一个pizza只需在简单工厂类中左修改
-
代码展示
pizza的代码与上相同,不再展示
OrderPizza
public class OrderPizza { //定义一个简单工厂对象 SimpleFactory simpleFactory; Pizza pizza = null; //构造器 public OrderPizza(SimpleFactory simpleFactory) { setFactory(simpleFactory); } public void setFactory(SimpleFactory simpleFactory) { String orderType = ""; //用户输入的 this.simpleFactory = simpleFactory; //设置简单工厂对象 do { orderType = getType(); pizza = this.simpleFactory.createPizza(orderType); //输出pizza if(pizza != null) { //订购成功 pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); } else { System.out.println(" 订购披萨失败 "); break; } }while(true); } // 写一个方法,可以获取客户希望订购的披萨种类 private String getType() { try { BufferedReader strin = new BufferedReader(new InputStreamReader(System.in)); System.out.println("input pizza 种类:"); String str = strin.readLine(); return str; } catch (IOException e) { e.printStackTrace(); return ""; } } }
PizzaStore
//相当于一个客户端,发出订购 public class PizzaStore { public static void main(String[] args) { //使用简单工厂模式 new OrderPizza(new SimpleFactory()); System.out.println("~~退出程序~~"); } }
SimpleFactory
//简单工厂类 public class SimpleFactory { //更加orderType 返回对应的Pizza 对象 public Pizza createPizza(String orderType) { Pizza pizza = null; System.out.println("使用简单工厂模式"); if (orderType.equals("greek")) { pizza = new GreekPizza(); pizza.setName(" 希腊披萨 "); } else if (orderType.equals("cheese")) { pizza = new CheesePizza(); pizza.setName(" 奶酪披萨 "); } else if (orderType.equals("pepper")) { pizza = new PepperPizza(); pizza.setName("胡椒披萨"); } return pizza; } //简单工厂模式 也叫 静态工厂模式 public static Pizza createPizza2(String orderType) { Pizza pizza = null; System.out.println("使用简单工厂模式2"); if (orderType.equals("greek")) { pizza = new GreekPizza(); pizza.setName(" 希腊披萨 "); } else if (orderType.equals("cheese")) { pizza = new CheesePizza(); pizza.setName(" 奶酪披萨 "); } else if (orderType.equals("pepper")) { pizza = new PepperPizza(); pizza.setName("胡椒披萨"); } return pizza; } }
-
工厂方法模式
**定义:**工厂方法模式:定义了一个创建对象的抽象方法,由子类决定要实例化的类。工厂方
法模式将对象的实例化推迟到子类。
实例讲解:
-
题目:披萨项目新的需求:客户在点披萨时,可以点不同口味的披萨,比如北京的奶酪pizza、北京的胡椒pizza 或者是伦敦的奶酪pizza、伦敦的胡椒pizza。
-
思路一:使用简单工厂模式,创建不同的简单工厂类,比如BJPizzaSimpleFactory、
LDPizzaSimpleFactory 等等.从当前这个案例来说,也是可以的,但是考虑到项目的
规模,以及软件的可维护性、可扩展性并不是特别好 -
思路二:工厂方法模式
-
UML
-
代码展示:
-
Order
BJOrderPizza
package factory.factorymethod.pizzastore.order; import factory.factorymethod.pizzastore.pizza.BJCheesePizza; import factory.factorymethod.pizzastore.pizza.BJPepperPizza; import factory.factorymethod.pizzastore.pizza.Pizza; public class BJOrderPizza extends OrderPizza { @Override Pizza createPizza(String orderType) { Pizza pizza = null; if(orderType.equals("cheese")) { pizza = new BJCheesePizza(); } else if (orderType.equals("pepper")) { pizza = new BJPepperPizza(); } // TODO Auto-generated method stub return pizza; } }
LDOrderPizza
package factory.factorymethod.pizzastore.order; import factory.factorymethod.pizzastore.pizza.LDCheesePizza; import factory.factorymethod.pizzastore.pizza.LDPepperPizza; import factory.factorymethod.pizzastore.pizza.Pizza; public class LDOrderPizza extends OrderPizza { @Override Pizza createPizza(String orderType) { Pizza pizza = null; if(orderType.equals("cheese")) { pizza = new LDCheesePizza(); } else if (orderType.equals("pepper")) { pizza = new LDPepperPizza(); } // TODO Auto-generated method stub return pizza; } }
OrderPizza
package factory.factorymethod.pizzastore.order; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import factory.factorymethod.pizzastore.pizza.Pizza; public abstract class OrderPizza { //定义一个抽象方法,createPizza , 让各个工厂子类自己实现 abstract Pizza createPizza(String orderType); // 构造器 public OrderPizza() { Pizza pizza = null; String orderType; // 订购披萨的类型 do { orderType = getType(); //抽象方法,由工厂子类完成,子类会继承父类,会先使用父类构造方法 pizza = createPizza(orderType); //输出pizza 制作过程 pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); } while (true); } // 写一个方法,可以获取客户希望订购的披萨种类 private String getType() { try { BufferedReader strin = new BufferedReader(new InputStreamReader(System.in)); System.out.println("input pizza 种类:"); String str = strin.readLine(); return str; } catch (IOException e) { e.printStackTrace(); return ""; } } }
PizzaStore
package factory.factorymethod.pizzastore.order; import java.util.Scanner; public class PizzaStore { public static void main(String[] args) { Scanner scanner=new Scanner(System.in); System.out.println("地区选择"); String loc = scanner.next(); if (loc.equals("bj")) { //创建北京口味的各种Pizza new BJOrderPizza(); } else { //创建伦敦口味的各种Pizza new LDOrderPizza(); } // TODO Auto-generated method stub } }
-
pizza
BJCheesePizza
public class BJCheesePizza extends Pizza { @Override public void prepare() { // TODO Auto-generated method stub setName("北京的奶酪pizza"); System.out.println(" 北京的奶酪pizza 准备原材料"); } }
BJPepperPizza
public class BJPepperPizza extends Pizza { @Override public void prepare() { // TODO Auto-generated method stub setName("北京的胡椒pizza"); System.out.println(" 北京的胡椒pizza 准备原材料"); } }
LDCheesePizza
public class LDCheesePizza extends Pizza{ @Override public void prepare() { // TODO Auto-generated method stub setName("伦敦的奶酪pizza"); System.out.println(" 伦敦的奶酪pizza 准备原材料"); } }
LDPepperPizza
public class LDPepperPizza extends Pizza{ @Override public void prepare() { // TODO Auto-generated method stub setName("伦敦的胡椒pizza"); System.out.println(" 伦敦的胡椒pizza 准备原材料"); } }
Pizza
package factory.factorymethod.pizzastore.pizza; //将Pizza 类做成抽象 public abstract class Pizza { protected String name; //名字 //准备原材料, 不同的披萨不一样,因此,我们做成抽象方法 public abstract void prepare(); public void bake() { System.out.println(name + " baking;"); } public void cut() { System.out.println(name + " cutting;"); } //打包 public void box() { System.out.println(name + " boxing;"); } public void setName(String name) { this.name = name; } }
-
-
-
缺点:若有新的地区需要在PizzaStore添加分支,若有多个披萨店则会十分麻烦
抽象工厂模式
-
基本介绍
- 抽象工厂模式:定义了一个interface用于创建相关或有依赖关系的对象簇,而无需指明具体的类
- 抽象工厂模式可以将简单工厂模式和工厂方法模式进行整合。
- 从设计层面看,抽象工厂模式就是对简单工厂模式的改进(或者称为进一步的抽象)。
- 将工厂抽象成两层,AbsFactory(抽象工厂) 和 具体实现的工厂子类。程序员可以根据创建对象类型使用对应的工厂子类。这样将单个的简单工厂类变成了工厂簇, 更利于代码的维护和扩展。
-
UML
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A9mgKAjp-1641114981171)(https://cdn.jsdelivr.net/gh/yfandragon/images/202111241811526.png)]
-
代码
-
pizza的和上面一致
-
AbsFactory
package factory.absfactory.pizzastore.order; import factory.absfactory.pizzastore.pizza.Pizza; //一个抽象工厂模式的抽象层(接口) public interface AbsFactory { //让下面的工厂子类来 具体实现 public Pizza createPizza(String orderType); }
-
LDFactory
package factory.absfactory.pizzastore.order; import factory.absfactory.pizzastore.pizza.LDCheesePizza; import factory.absfactory.pizzastore.pizza.LDPepperPizza; import factory.absfactory.pizzastore.pizza.Pizza; public class LDFactory implements AbsFactory { @Override public Pizza createPizza(String orderType) { System.out.println("~使用的是抽象工厂模式~"); Pizza pizza = null; if (orderType.equals("cheese")) { pizza = new LDCheesePizza(); } else if (orderType.equals("pepper")) { pizza = new LDPepperPizza(); } return pizza; } }
-
BJFactory
package factory.absfactory.pizzastore.order; import factory.absfactory.pizzastore.pizza.BJCheesePizza; import factory.absfactory.pizzastore.pizza.BJPepperPizza; import factory.absfactory.pizzastore.pizza.Pizza; //这是工厂子类 public class BJFactory implements AbsFactory { @Override public Pizza createPizza(String orderType) { System.out.println("~使用的是抽象工厂模式~"); // TODO Auto-generated method stub Pizza pizza = null; if(orderType.equals("cheese")) { pizza = new BJCheesePizza(); } else if (orderType.equals("pepper")){ pizza = new BJPepperPizza(); } return pizza; } }
-
OrderPizza
package factory.absfactory.pizzastore.order; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import factory.absfactory.pizzastore.pizza.Pizza; public class OrderPizza { AbsFactory factory; // 构造器 //factory可以使AbsFactory的子类会自动转型 public OrderPizza(AbsFactory factory) { setFactory(factory); } private void setFactory(AbsFactory factory) { Pizza pizza = null; String orderType = ""; // 用户输入 this.factory = factory; do { orderType = getType(); // factory 可能是北京的工厂子类,也可能是伦敦的工厂子类 pizza = factory.createPizza(orderType); if (pizza != null) { // 订购ok pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); } else { System.out.println("订购失败"); break; } } while (true); } // 写一个方法,可以获取客户希望订购的披萨种类 private String getType() { try { BufferedReader strin = new BufferedReader(new InputStreamReader(System.in)); System.out.println("input pizza 种类:"); String str = strin.readLine(); return str; } catch (IOException e) { e.printStackTrace(); return ""; } } }
-
PizzaStore
package factory.absfactory.pizzastore.order; public class PizzaStore { public static void main(String[] args) { // TODO Auto-generated method stub //new OrderPizza(new BJFactory()); new OrderPizza(new LDFactory()); } }
-
-
代码解析:
- 工厂地区的选择,因为都实现了抽象工厂,只需要在订购时确认即可,工厂方法中需要在商店中进行地区选择。
- 将选择不同口味的披萨这一操作同一放在工厂中执行,这是使用了简单工厂模式
将工厂类的实例化推迟到子类进行,这是使用了工厂方法模式
原型模式
克隆羊问题
-
题目:
现在有一只羊tom,姓名为: tom, 年龄为:1,颜色为:白色,请编写程序创建和tom羊属性完全相同的10只羊。
-
传统方式:
public class Client { public static void main(String[] args) { Sheep sheep = new Sheep("tom","white", 1); Sheep sheep2 = new Sheep(sheep.getName(), sheep.getColor(), sheep.getAge()); Sheep sheep3 = new Sheep(sheep.getName(), sheep.getColor(), sheep.getAge()); Sheep sheep4 = new Sheep(sheep.getName(), sheep.getColor(), sheep.getAge()); Sheep sheep5 = new Sheep(sheep.getName(), sheep.getColor(), sheep.getAge()); Sheep sheep6 = new Sheep(sheep.getName(), sheep.getColor(), sheep.getAge()); System.out.println(sheep2); System.out.println(sheep3); System.out.println(sheep4); System.out.println(sheep5); System.out.println(sheep6); } }
-
传统的方式的优缺点
-
优点是比较好理解,简单易操作。
-
在创建新的对象时,总是需要重新获取原始对象的属性,如果创建的对象比较复杂时,效率较低
//若是有20个属性需要get20次,太麻烦 Sheep sheep5 = new Sheep(sheep.getName(), sheep.getColor(), sheep.getAge());
-
总是需要重新初始化对象,而不是动态地获得对象运行时的状态, 不够灵活
Sheep sheep = new Sheep("tom","white", 1); //克隆 Sheep sheep2 = new Sheep(sheep.getName(), sheep.getColor(), sheep.getAge()); //本体年龄改变但是克隆体年龄不变,除非再新建一个克隆体 sheep.setAge(20);
-
改进思路:
Java中Object类是所有类的根类,Object类提供了一个clone()方法,该方法可以
将一个Java对象复制一份,但是需要实现clone的Java类必须要实现一个接口Cloneable,
该接口表示该类能够复制且具有复制的能力,由此引出原型模式。
-
-
原型模式基本介绍
- 原型模式(Prototype模式)是指:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象
- 原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象, 无需知道如何创建的细节
- 工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建,即 对象.clone()
-
原型模式-原理结构图(UML类图)
-
原理结构图说明:
- Prototype : 原型类,声明一个克隆自己的接口
- ConcretePrototype: 具体的原型类, 实现一个克隆自己的操作
- Client: 让一个原型对象克隆自己,从而创建一个新的对象(属性一样)
-
uml类图
-
原型模式实现克隆羊
Sheep
package prototype; public class Sheep implements Cloneable{ private String name; private String color; private int age; public Sheep(String name, String color, int age) { super(); this.name = name; this.color = color; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Sheep [name=" + name + ", color=" + color + ", age=" + age + "]"; } @Override //克隆该实例,使用默认clone方法来完成 protected Object clone() { Sheep sheep=null; try { sheep=(Sheep)super.clone(); } catch (Exception e) { // TODO: handle exception System.out.println(e.getMessage()); } return sheep; } }
Client1
package prototype; public class Client1 { public static void main(String[] args) { System.out.println("原型模式进行克隆"); Sheep sheep=new Sheep("tom","white", 1); Sheep sheep2=(Sheep)sheep.clone(); Sheep sheep3=(Sheep)sheep.clone(); Sheep sheep4=(Sheep)sheep.clone(); Sheep sheep5=(Sheep)sheep.clone(); Sheep sheep6=(Sheep)sheep.clone(); System.out.println(sheep2); System.out.println(sheep3); System.out.println(sheep4); System.out.println(sheep5); System.out.println(sheep6); } }
此时属性发生变化,克隆的对象会动态同步
浅拷贝与深拷贝
-
浅拷贝
-
对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。
-
对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类 的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内 存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成 员变量值
-
前面我们克隆羊就是浅拷贝,浅拷贝是使用默认的 clone()方法来实现
sheep = (Sheep) super.clone();
-
浅拷贝拷贝应用类型成员变量实例
Sheep
public class Sheep implements Cloneable{ Sheep friend; @Override protected Object clone() throws CloneNotSupportedException { Sheep sheep=null; sheep=(Sheep) super.clone(); return sheep; } }
Client
public class Client { public static void main(String[] args) throws CloneNotSupportedException { Sheep sheep=new Sheep(); Sheep sheep2=new Sheep(); sheep.friend=sheep2; Sheep clonesheep=(Sheep) sheep.clone(); Sheep clonesheep1=(Sheep) sheep.clone(); Sheep clonesheep2=(Sheep) sheep.clone(); System.out.println(clonesheep.friend.hashCode()); System.out.println(clonesheep1.friend.hashCode()); System.out.println(clonesheep2.friend.hashCode()); } }
结果:
拷贝对象的引用类型属性friend地址一致都指向同一个实例
-
-
深拷贝
-
深拷贝基本介绍
- 复制对象的所有基本数据类型的成员变量值
- 为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。也就是说,对象进行深拷贝要对整个对象进行拷贝。
-
深拷贝实现方式1:重写clone方法来实现深拷贝
此方法使用起来较为麻烦,若有多个引用类型的属性需要对多个引用类型的属性进行处理。-
DeepProtoType
package prototype.prototype.deepclone; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public class DeepProtoType implements Serializable, Cloneable{ public String name; //String 属性 public DeepCloneableTarget deepCloneableTarget;// 引用类型 public DeepProtoType() { super(); } //深拷贝 - 方式 1 使用clone 方法 @Override protected Object clone() throws CloneNotSupportedException { Object deep = null; //这里完成对基本数据类型(属性)和String的克隆 deep = super.clone(); //对引用类型的属性,进行单独处理 DeepProtoType deepProtoType = (DeepProtoType)deep; deepProtoType.deepCloneableTarget = (DeepCloneableTarget)deepCloneableTarget.clone(); // TODO Auto-generated method stub return deepProtoType; } }
25到28行对引用类型的属性进行深拷贝,使用要拷贝的引用类型属性自己clone()方法对自己进行拷贝,因为clone方法的参数是object因此再强制转型为该属性自己的引用类型然后赋给浅拷贝创建对象的该引用类型属性。
-
DeepCloneableTarget(拷贝的对象的引用数据类型)
package prototype.prototype.deepclone; import java.io.Serializable; public class DeepCloneableTarget implements Serializable, Cloneable { static final long serialVersionUID = 1L; private String cloneName; private String cloneClass; // 构造器 public DeepCloneableTarget(String cloneName, String cloneClass) { this.cloneName = cloneName; this.cloneClass = cloneClass; } // 因为该类的属性,都是String没有引用类型属性, 因此我们这里使用默认的clone完成即可 @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } }
-
Client
package prototype.prototype.deepclone; public class Client { public static void main(String[] args) throws Exception { // TODO Auto-generated method stub DeepProtoType p = new DeepProtoType(); p.name = "宋江"; p.deepCloneableTarget = new DeepCloneableTarget("大牛", "小牛"); //方式1 完成深拷贝 DeepProtoType p2 = (DeepProtoType) p.clone(); System.out.println("p.name=" + p.name + "p.deepCloneableTarget=" + p.deepCloneableTarget.hashCode()); System.out.println("p2.name=" + p.name + "p2.deepCloneableTarget=" + p2.deepCloneableTarget.hashCode()); } }
两个对象引用数据类型属性的地址不同,说明原对象和克隆对象的该属性不是指向同一个deepCloneableTarget,深拷贝成功
-
-
深拷贝实现方式2:通过对象序列化实现深拷贝
通过输入输出流,将该对象输出后读入时自然会创建一个新的对象,因为输出时引用类型也会输出,因此这个新的对象完成深拷贝。-
DeepProtoType
package prototype.prototype.deepclone; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public class DeepProtoType implements Serializable, Cloneable{ public String name; //String 属性 public DeepCloneableTarget deepCloneableTarget;// 引用类型 public DeepProtoType() { super(); } public Object deepClone() { //创建流对象 ByteArrayOutputStream bos = null; ObjectOutputStream oos = null; ByteArrayInputStream bis = null; ObjectInputStream ois = null; try { //序列化 bos = new ByteArrayOutputStream(); oos = new ObjectOutputStream(bos); //当前这个类DeepProtoType的对象以对象流的方式输出 //DeepProtoType的引用类型属性自然也会被输出 oos.writeObject(this); //反序列化 bis = new ByteArrayInputStream(bos.toByteArray()); ois = new ObjectInputStream(bis); //读取刚刚输出的时自然会创建新的DeepProtoType。 DeepProtoType copyObj = (DeepProtoType)ois.readObject(); return copyObj; } catch (Exception e) { // TODO: handle exception e.printStackTrace(); return null; } finally { //关闭流 try { bos.close(); oos.close(); bis.close(); ois.close(); } catch (Exception e2) { // TODO: handle exception System.out.println(e2.getMessage()); } } } }
-
DeepCloneableTarget同上
-
Client
package prototype.prototype.deepclone; public class Client { public static void main(String[] args) throws Exception { // TODO Auto-generated method stub DeepProtoType p = new DeepProtoType(); p.name = "宋江"; p.deepCloneableTarget = new DeepCloneableTarget("大牛", "小牛"); // 方式2 完成深拷贝 DeepProtoType p2 = (DeepProtoType) p.deepClone(); System.out.println("p.name=" + p.name + "p.deepCloneableTarget=" + p.deepCloneableTarget.hashCode()); System.out.println("p2.name=" + p.name + "p2.deepCloneableTarget=" + p2.deepCloneableTarget.hashCode()); } }
两个对象引用数据类型属性的地址不同,说明原对象和克隆对象的该属性不是指向同一个deepCloneableTarget,深拷贝成功
-
-
建造者模式
建造者模式概念
-
基本介绍
- 建造者模式(Builder Pattern) 又叫生成器模式,是一种对象构建模式。它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方 法可以构造出不同表现(属性)的对象。
- 建造者模式 是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。
- 对于造房子,用户只需要知道他选色普通住房还是别墅,不需要看到房子是怎么造的,也就是说对于不应该在房子类中放入造房子的方法。
-
建造者模式的四个角色
- Product(产品角色): 一个具体的产品对象。
- Builder(抽象建造者): 创建一个Product对象的各个部件指定的 接口/抽象类。只需要定建造的流程,具体的细节交给具体建造者
- ConcreteBuilder(具体建造者): 实现接口,构建和装配各个部件。
- Director(指挥者): 构建一个使用Builder接口的对象。它主要是用于创建一个复杂的对象。它主要有两个作用,
一是:隔离了客户与对象的生产过程,
二是: 负责控制产品对象的生产过程
-
建造者模式原理类图
造房子问题
需求
- 需要建房子:这一过程为打桩、砌墙、封顶
- 房子有各种各样的,比如普通房,高楼,别墅,各种房子的过程虽然一样,但是 要求不要相同的.
-
传统方法
-
UML
产品角色:房子
抽象建造者:AbstractHouse
具体建造者:CommonHouse -
代码
AbstractHouse
public abstract class AbstractHouse { //打地基 public abstract void buildBasic(); //砌墙 public abstract void buildWalls(); //封顶 public abstract void roofed(); public void build() { buildBasic(); buildWalls(); roofed(); } }
CommonHouse
public class CommonHouse extends AbstractHouse { @Override public void buildBasic() { // TODO Auto-generated method stub System.out.println(" 普通房子打地基 "); } @Override public void buildWalls() { // TODO Auto-generated method stub System.out.println(" 普通房子砌墙 "); } @Override public void roofed() { // TODO Auto-generated method stub System.out.println(" 普通房子封顶 "); } }
Client
public class Client { public static void main(String[] args) { // TODO Auto-generated method stub CommonHouse commonHouse = new CommonHouse(); commonHouse.build(); } }
-
缺点:
- 设计的程序结构,过于简单,没有设计缓存层对象,程序的扩展和维护不好。
- 这种设计方案,把产品(即:房子CommeHouse) 和 创建产品的过程(即:建房子流程从父类继承的方法) 封装在一起,耦合性增强了。
-
改进:将产品和产品建造过程解耦(建造者模式)
-
-
改进使用建造者模式
将产品与制作流程解耦分离。
-
UML
-
House(产品)
此时没有决定是什么类型的房子,普通还是公寓会在建造是决定,也不知道地基,屋顶,墙是怎么造的,客户只需要入住,不需要知道怎么造的。
public class House { private String baise; private String wall; private String roofed; public String getBaise() { return baise; } public void setBaise(String baise) { this.baise = baise; } 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; } }
-
HouseBuilder(抽象的建造者)
先制定流程,具体实现在具体建造者中。
// 抽象的建造者 public abstract class HouseBuilder { protected House house = new House(); //将建造的流程写好, 抽象的方法 public abstract void buildBasic(); public abstract void buildWalls(); public abstract void roofed(); //建造房子好, 将产品(房子) 返回 public House buildHouse() { return house; } }
-
CommonHouse(具体的建造者)
具体实现是给什么制作什么房子,此时将制作流程与产品本身分开了,制作流程的变化比如说造个别墅,不影响产品类也不影响抽象建造者(封装了产品)。public class CommonHouse extends HouseBuilder { @Override public void buildBasic() { // TODO Auto-generated method stub System.out.println(" 普通房子打地基5米 "); } @Override public void buildWalls() { // TODO Auto-generated method stub System.out.println(" 普通房子砌墙10cm "); } @Override public void roofed() { // TODO Auto-generated method stub System.out.println(" 普通房子屋顶 "); } }
-
HighBuilding同上
-
HouseDirector(指挥者)
指定制作流程,调整造房子砌墙,造屋顶,打地基的顺序。
//指挥者,这里去指定制作流程,返回产品 public class HouseDirector { HouseBuilder houseBuilder = null; //构造器传入 houseBuilder public HouseDirector(HouseBuilder houseBuilder) { this.houseBuilder = houseBuilder; } //通过setter 传入 houseBuilder public void setHouseBuilder(HouseBuilder houseBuilder) { this.houseBuilder = houseBuilder; } //如何处理建造房子的流程,交给指挥者 public House constructHouse() { houseBuilder.buildBasic(); houseBuilder.buildWalls(); houseBuilder.roofed(); return houseBuilder.buildHouse(); } }
-
Client
public class Client { public static void main(String[] args) { //盖普通房子 CommonHouse commonHouse = new CommonHouse(); //准备创建房子的指挥者 HouseDirector houseDirector = new HouseDirector(commonHouse); //完成盖房子,返回产品(普通房子) House house = houseDirector.constructHouse(); //System.out.println("输出流程"); System.out.println("--------------------------"); //盖高楼 HighBuilding highBuilding = new HighBuilding(); //重置建造者 houseDirector.setHouseBuilder(highBuilding); //完成盖房子,返回产品(高楼) houseDirector.constructHouse(); } }
-
源码中建造者模式角色分析
- Appendable 接口定义了多个append方法(抽象方法), 即Appendable 为抽象建 造者, 定义了抽象方法
- AbstractStringBuilder 实现了 Appendable 接口方法,这里的 AbstractStringBuilder 已经是建造者,只是不能实例化
- StringBuilder 即充当了指挥者角色,同时充当了具体的建造者,建造方法的实现是由 AbstractStringBuilder 完成, 而StringBuilder 继承了 AbstractStringBuilder
直接使用了父类的append所以也有建造者的作用
结构型模式
适配器模式
-
基本介绍
- 适配器模式(Adapter Pattern)将某个类的接口转换成客户端期望的另一个接口表示,主的目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作。其别名为包装器(Wrapper)
- 主要分为三类:类适配器模式、对象适配器模式、接口适配器模式
-
工作原理
- 适配器模式:将一个类的接口转换成另一种接口.让原本接口不兼容的类可以兼容
- 从用户的角度看不到被适配者,是解耦的
- 用户调用适配器转化出来的目标接口方法,适配器再调用被适配者的相关接口方法
- 用户收到反馈结果,感觉只是和目标接口交互,如图
类适配器模式
基本介绍:Adapter类,通过继承 src类,实现 dst 类接口,完成src->dst的适配。
-
案例:
以生活中充电器的例子来讲解适配器,充电器本身相当于Adapter,220V交流电 相当于src (即被适配者),我们的目dst(即 目标)是5V直流电
-
UML
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QAeN1lkD-1641114981179)(https://cdn.jsdelivr.net/gh/yfandragon/images/202112022319206.png)]
-
代码:
Voltage220V(被适配的类)
//被适配的类 public class Voltage220V { //输出220V的电压 public int output220V() { int src = 220; System.out.println("电压=" + src + "伏"); return src; } }
IVoltage5V(适配接口)
//适配接口 public interface IVoltage5V { public int output5V(); }
VoltageAdapter(适配器类)
//适配器类 public class VoltageAdapter extends Voltage220V implements IVoltage5V { @Override public int output5V() { // TODO Auto-generated method stub //获取到220V电压 int srcV = output220V(); int dstV = srcV / 44 ; //转成 5v return dstV; } }
Phone
public class Phone { //充电 使用5v的充电 public void charging(IVoltage5V iVoltage5V) { if(iVoltage5V.output5V() == 5) { System.out.println("电压为5V, 可以充电~~"); } else if (iVoltage5V.output5V() > 5) { System.out.println("电压大于5V, 不能充电~~"); } } }
client
public class Client { public static void main(String[] args) { // TODO Auto-generated method stub System.out.println(" === 类适配器模式 ===="); Phone phone = new Phone(); //给手机充电 phone.charging(new VoltageAdapter()); } }
-
-
类适配器模式注意事项和细节
- Java是单继承机制,所以类适配器需要继承src类(耦合度高)这一点算是一个缺点, 因为这要求dst必须是接口有一定局限性;
- src类的方法在Adapter中都会暴露出来,也增加了使用的成本。
- 由于其继承了src类,所以它可以根据需求重写src类的方法,使得Adapter的灵 活性增强了
对象适配器模式
-
对象适配器模式介绍
- 基本思路和类的适配器模式相同,只是将Adapter类作修改,不是继承src类,而是持有src类的实例,以解决兼容性的问题。 即:持有 src类,实现 dst 类接口, 完成src->dst的适配
- 根据“合成复用原则”,在系统中尽量使用关联关系来替代继承关系。
- 对象适配器模式是适配器模式常用的一种
-
实例
-
题目同上
-
UML
-
代码
VoltageAdapter//适配器类 public class VoltageAdapter implements IVoltage5V { private Voltage220V voltage220V; // 关联关系-聚合 //通过构造器,传入一个 Voltage220V 实例 public VoltageAdapter(Voltage220V voltage220v) { this.voltage220V = voltage220v; } @Override public int output5V() { int dst = 0; if(null != voltage220V) { int src = voltage220V.output220V();//获取220V 电压 System.out.println("使用对象适配器,进行适配~~"); dst = src / 44; System.out.println("适配完成,输出的电压为=" + dst); } return dst; } }
client
public class Client { public static void main(String[] args) { // TODO Auto-generated method stub System.out.println(" === 对象适配器模式 ===="); Phone phone = new Phone(); phone.charging(new VoltageAdapter(new Voltage220V())); } }
其余类同上
-
-
对象适配器模式注意事项和细节
- 对象适配器和类适配器其实算是同一种思想,只不过实现方式不同。 根据合成复用原则,使用组合替代继承, 所以它解决了类适配器必须继承src的 局限性问题,也不再要求dst必须是接口。
- 使用成本更低,更灵活
接口适配器模式
- 接口适配器模式介绍
当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接 口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求
桥接模式
介绍
-
基本介绍
- 桥接模式(Bridge模式)是指:将实现与抽象放在两个不同的类层次中,使两个层次可以独立改变。
- 是一种结构型设计模式
- Bridge模式基于类的最小设计原则,通过使用封装、聚合及继承等行为让不同的类承担不同的职责。它的主要特点是把抽象(Abstraction)与行为实现 (Implementation)分离开来,从而可以保持各部分的独立性以及应对他们的功能扩展。
-
桥接模式(Bridge)-原理类图
原理类图说明:
- Client类:桥接模式的调用者
- 抽象类(Abstraction):维护了 Implementor/即它的实现类ConcretelmplementorA…, 二者是聚合关系, Abstraction 充当桥接类
- RefinedAbstraction:是Abstraction抽象类的子类
- Implementor:行为实现类的接口
- ConcretelmplementorA /B:行为的具体实现类
- 这里的抽象类和接口是聚合的关系,其实是调用与被调用的关系。
实例讲解
-
题目:
-
传统解决方案
-
UML
-
传统方案解决手机操作问题分析
- 扩展性问题(类爆炸),如果我们再增加手机的样式(旋转式),就需要增加各个品牌手机的类,同样如果我们增加一个手机品牌,也要在各个手机样式类下增加。
- 违反了单一职责原则,当我们增加手机样式时,要同时增加所有品牌的手机,这样增加了代码维护成本.
- 解决方案-使用桥接模式
-
-
桥接模式解决手机操作问题
-
UML
此时由图可知,增加一个手机样式,只需在phone下加一个类,其他位置都不用动
-
代码:
Brand
public interface Brand { void open(); void close(); void call(); }
Xiaomi
public class Xiaomi implements Brand{ @Override public void open() { // TODO Auto-generated method stub System.out.println("小米手机开机"); } @Override public void close() { // TODO Auto-generated method stub System.out.println("小米手机关机"); } @Override public void call() { // TODO Auto-generated method stub System.out.println("小米手机打电话"); } }
Vivio
package adapter.my1; public class Vivo implements Brand{ @Override public void open() { // TODO Auto-generated method stub System.out.println("vivo手机开机"); } @Override public void close() { // TODO Auto-generated method stub System.out.println("vivo手机关机"); } @Override public void call() { // TODO Auto-generated method stub System.out.println("vivo手机打电话"); } }
Phone
package adapter.my1; //充当桥的作用将接口与子类连通,又低耦合 public abstract class Phone { //组合品牌 private Brand brand; //构造器 public Phone(Brand brand) { System.out.println("手机父类"); this.brand = brand; } protected void open() { this.brand.open(); } protected void close() { this.brand.close(); } protected void call() { this.brand.call(); } }
FoldedPhone
package adapter.my1; public class FoldedPhone extends Phone{ //构造器无法继承,要实现,用于获取手机品牌 public FoldedPhone(Brand brand) { super(brand); System.out.println("折叠子类"); } @Override protected void open() { // TODO Auto-generated method stub super.open(); System.out.println("折叠式手机"); } @Override protected void close() { // TODO Auto-generated method stub super.close(); System.out.println("折叠式手机"); } @Override protected void call() { // TODO Auto-generated method stub super.call(); System.out.println("折叠式手机"); } }
client
public class Client { public static void main(String[] args) { //获取折叠式手机(样式+品牌) Phone Phone=new FoldedPhone(new Xiaomi()); Phone.call(); } }
-
注意与应用场景
-
桥接模式的注意事项和细节
- 实现了抽象和实现部分的分离,从而极大的提供了系统的灵活性,让抽象部分和实现部分独立开来,这有助于系统进行分层设计,从而产生更好的结构化系统。
- 对于系统的高层部分,只需要知道抽象部分和实现部分的接口就可以了,其它的部分由具体业务来完成。
- 桥接模式替代多层继承方案,可以减少子类的个数,降低系统的管理和维护成本
- 桥接模式的引入增加了系统的理解和设计难度,由于聚合关联关系建立在抽象层, 要求开发者针对抽象进行设计和编程
- 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围有一定的局 限性,即需要有这样的应用场景。
-
桥接模式其它应用场景
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-reCPsHHa-1641114981183)(https://cdn.jsdelivr.net/gh/yfandragon/images/202112060843012.png)]
装饰者模式
介绍
-
定义:
装饰者模式:动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则(ocp)
-
装饰者模式原理
- 装饰者模式就像打包一个快递
- 主体:比如:陶瓷、衣服 (Component) // 被装饰者
- 包装:比如:报纸填充、塑料泡沫、纸板、木板(Decorator) - ConcreteComponent和Decorator ConcreteComponent:具体的主体, 比如前面的各个单品咖啡
- Decorator: 装饰者,比如各调料。
- 在如图的Component与ConcreteComponent之间,如果 ConcreteComponent类很多,还可以设计一个缓冲层,将共有的部分提取出来, 抽成个类。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nIUiNL1u-1641114981184)(https://cdn.jsdelivr.net/gh/yfandragon/images/202112060938587.png)]
- 装饰者模式就像打包一个快递
实例讲解
-
题目
-
传统解决方案一
-
UML
-
问题分析:
- Drink 是一个抽象类,表示饮料
- des就是对咖啡的描述, 比如咖啡的名字
- cost() 方法就是计算费用,Drink 类中做成一个抽象方法
- Decaf 就是单品咖啡, 继承Drink, 并实现cost
- Espress && Milk 就是单品咖啡+调料, 这个组合很多
- 问题:这样设计,会有很多类,当我们增加一个单品咖啡,或者一个新的调料, 类的数量就会倍增,就会出现类爆炸
-
-
传统解决方案二
-
UML
-
问题分析
- 方案2可以控制类的数量,不至于造成很多的类
- 在增加或者删除调料种类时,代码的维护量很大,每种咖啡类都得做增加或删除调料。
- 考虑到用户可以添加多份调料时,可以将hasMilk 返回一个对应int 4) 考虑使用 装饰者 模式
-
-
装饰者模式
-
设计的方案图
下图是Decorater继承Drink写反了
-
装饰者模式下的订单:2份巧克力+一份牛奶的LongBlack
说明:
- Milk包含了LongBlack (longBlack为被装饰者)
- 一份Chocolate包含了(Milk+LongBlack) (Milk+LongBlack)为被装饰者
- 一份Chocolate包含了(Chocolate+Milk+LongBlack) (Chocolate+Milk+LongBlack)为被装饰者
- 这样不管是什么形式的单品咖啡+调料组合,通过递归方式可以方便的组合和维护
-
代码:
Coffee
public class Coffee extends Drink { @Override public float cost() { // TODO Auto-generated method stub return super.getPrice(); } }
Espresso
public class Espresso extends Coffee { public Espresso() { setDes(" 意大利咖啡 "); setPrice(6.0f); } }
Drink
public abstract class Drink { public String des; // 描述 private float price = 0.0f; public String getDes() { return des; } public void setDes(String des) { this.des = des; } public float getPrice() { return price; } public void setPrice(float price) { this.price = price; } //计算费用的抽象方法 //子类来实现 public abstract float cost(); }
Decorator
public class Decorator extends Drink { private Drink obj; public Decorator(Drink obj) { //组合 // TODO Auto-generated constructor stub this.obj = obj; } @Override public float cost() { // TODO Auto-generated method stub // getPrice 自己(调料)价格 return getPrice() + obj.cost(); } @Override public String getDes() { // TODO Auto-generated method stub // obj.getDes() 输出被装饰者的信息 return des + " " + getPrice() + " && " + obj.getDes(); } }
Chocolate
//具体的Decorator, 这里就是调味品 public class Chocolate extends Decorator { public Chocolate(Drink obj) { super(obj); setDes(" 巧克力 "); setPrice(3.0f); // 调味品 的价格 } }
CoffeeBar
public class CoffeeBar { public static void main(String[] args) { // TODO Auto-generated method stub // 装饰者模式下的订单:2份巧克力+一份牛奶的LongBlack // 1. 点一份 LongBlack Drink order = new LongBlack(); System.out.println("费用1=" + order.cost()); System.out.println("描述=" + order.getDes()); // 2. order 加入一份牛奶 order = new Milk(order); System.out.println("order 加入一份牛奶 费用 =" + order.cost()); System.out.println("order 加入一份牛奶 描述 = " + order.getDes()); // 3. order 加入一份巧克力 order = new Chocolate(order); System.out.println("order 加入一份牛奶 加入一份巧克力 费用 =" + order.cost()); System.out.println("order 加入一份牛奶 加入一份巧克力 描述 = " + order.getDes()); // 3. order 加入一份巧克力 order = new Chocolate(order); System.out.println("order 加入一份牛奶 加入2份巧克力 费用 =" + order.cost()); System.out.println("order 加入一份牛奶 加入2份巧克力 描述 = " + order.getDes()); System.out.println("==========================="); Drink order2 = new DeCaf(); System.out.println("order2 无因咖啡 费用 =" + order2.cost()); System.out.println("order2 无因咖啡 描述 = " + order2.getDes()); order2 = new Milk(order2); System.out.println("order2 无因咖啡 加入一份牛奶 费用 =" + order2.cost()); System.out.println("order2 无因咖啡 加入一份牛奶 描述 = " + order2.getDes()); }
-
组合模式
介绍
- 组合模式(Composite Pattern),又叫部分整体模式,它创建了对象组的树形结构,将对象组合成树状结构以表示“整体-部分”的层次关系。
- 组合模式依据树形结构来组合对象,用来表示部分以及整体层次。
- 这种类型的设计模式属于结构型模式。
- 组合模式使得用户对单个对象和组合对象的访问具有一致性,即:组合能让客户以一致的方式处理个别对象以及组合对象
- 组合模式解决这样的问题,当我们的要处理的对象可以生成一颗树形结构,而我们要对树上的节点和叶子进行操作时,它能够提供一致的方式,而不用考虑它是节点还是叶子。
原理类图
-
Component:这是组合中对象声明接口(可以是抽象类或接口),在适当情况下,实现所有类共有的接口默认行为,用于访问和管理Component的子部件。
-
leaf:被管理者,没有子节点
-
Composite:非叶子节点,用于存储子部件(管理者),实现Component中子部件的相关操作。
实例讲解
-
案例介绍
编写程序展示一个学校院系结构:需求是这样,要在一个页面中展示出学校的院系组成,一个学校有多个学院,一个学院有多个系。如图:
-
传统方式存在问题及解决方案
-
将学院看做是学校的子类,系是学院的子类,这样实际上是站在组织大小来进行分层次的
-
实际上我们的要求是 :在一个页面中展示出学校的院系组成,一个学校有多个学院,一个学院有多个系, 因此这种方案,不能很好实现的管理的操作,因为父类不能管理子类。
-
把学校、院、系都看做是组织结构,他们之间没有继承的关系,而是一个树形结构,可以更好的实现管理操作。(组合模式)
-
-
组合模式解决
-
UML
University中聚合了College
College中聚合了Department
-
代码:
OrganizationComponent
public abstract class OrganizationComponent { private String name; private String intro; public OrganizationComponent(String name, String intro) { super(); this.name = name; this.intro = intro; } //为什么不写成抽象方法,为了不让叶子节点重新实现 protected void add(OrganizationComponent organizationComponent) { //默认实现 throw new UnsupportedOperationException(); } protected void remove(OrganizationComponent organizationComponent) { //默认实现 throw new UnsupportedOperationException(); } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getIntro() { return intro; } public void setIntro(String intro) { this.intro = intro; } //方法print, 做成抽象的子类都需要实现 protected abstract void print(); }
University
public class University extends OrganizationComponent{ public University(String name, String intro) { super(name, intro); // TODO Auto-generated constructor stub } //存放的是College University聚合了College List<OrganizationComponent> organizationComponents= new ArrayList<OrganizationComponent>(); //重写add @Override protected void add(OrganizationComponent organizationComponent) { // TODO Auto-generated method stub organizationComponents.add(organizationComponent); } //重写remove @Override protected void remove(OrganizationComponent organizationComponent) { // TODO Auto-generated method stub organizationComponents.remove(organizationComponent); } @Override public String getName() { // TODO Auto-generated method stub return super.getName(); } @Override public String getIntro() { // TODO Auto-generated method stub return super.getIntro(); } //print:输出university包含的学院 @Override protected void print() { // TODO Auto-generated method stub System.out.println("-----------"+getName()+"---------"); //遍历organizationComponents for (OrganizationComponent organizationComponent : organizationComponents) { organizationComponent.print(); } } }
College
public class College extends OrganizationComponent { public College(String name, String intro) { super(name, intro); // TODO Auto-generated constructor stub } //存放的是Department Colleg聚合了Department List<OrganizationComponent> organizationComponents= new ArrayList<OrganizationComponent>(); //重写add @Override protected void add(OrganizationComponent organizationComponent) { // TODO Auto-generated method stub organizationComponents.add(organizationComponent); } //重写remove @Override protected void remove(OrganizationComponent organizationComponent) { // TODO Auto-generated method stub organizationComponents.remove(organizationComponent); } @Override public String getName() { // TODO Auto-generated method stub return super.getName(); } @Override public String getIntro() { // TODO Auto-generated method stub return super.getIntro(); } //print:输出university包含的学院 @Override protected void print() { // TODO Auto-generated method stub System.out.println("-----------"+getName()+"---------"); //遍历organizationComponents for (OrganizationComponent organizationComponent : organizationComponents) { organizationComponent.print(); } } }
Department
public class Department extends OrganizationComponent{ public Department(String name, String intro) { super(name, intro); // TODO Auto-generated constructor stub } @Override protected void print() { // TODO Auto-generated method stub System.out.println(getName()); } @Override public String getName() { // TODO Auto-generated method stub return super.getName(); } @Override public String getIntro() { // TODO Auto-generated method stub return super.getIntro(); } }
Client
public class Client { public static void main(String[] args) { //从大到小,创建对象 OrganizationComponent university = new University("清华大学", "中国顶尖大学"); OrganizationComponent college = new College("计算机学院", "计算机学院"); OrganizationComponent college1 = new College("信息工程学院", "信息工程学院"); //计算机加专业 college.add(new Department("软件工程", "软件工程")); college.add(new Department("网络工程", "网络工程")); college.add(new Department("计算机科学与技术", "计算机科学与技术")); //信息工程加专业 college1.add(new Department("通信工程","通信工程" )); college1.add(new Department("信息工程","信息工程" )); //学校加学院 university.add(college1); university.add(college); university.print(); } }
-
外观模式
介绍与原理类图
-
介绍
- 外观模式(Facade),也叫“过程模式:外观模式为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
- 外观模式通过定义一个一致的接口,用以屏蔽内部子系统的细节,使得调用端只需跟这个接口发生调用,而无需关心这个子系统的内部细节。
-
原理类图
- 外观类(Facade):为调用端提供统一的调用接口,知道那些子系统负责处理请求从而将调用端的请求代理给适当子系统对象
- 调用者(Client): 外观接口的调用者
- 子系统的集合:指模块或者子系统,处理Facade 对象指派的任务,他是功能的实际提供者。
实例讲解
-
案例介绍
-
传统方式解决影院管理问题分析
-
在ClientTest 的main方法中,创建各个子系统的对象,并直接去调用子系统(对象) 相关方法,会造成调用过程混乱,没有清晰的过程
-
不利于在ClientTest 中,去维护对子系统的操作
-
解决思路:定义一个高层接口,给子系统中的一组接口提供一个一致的界面(比如在高层接口提供四个方法 ready, play, pause, end ),用来访问子系统中的一群接口
也就是说 就是通过定义一个一致的接口(界面类),用以屏蔽内部子系统的细节,使得调用端只需跟这个接口发生调用,而无需关心这个子系统的内部细节 => 外观模式
-
-
外观模式解决
-
UML
-
代码:
HomeTheaterFacade
public class HomeTheaterFacade { //定义各个子系统对象 private TheaterLight theaterLight; private Popcorn popcorn; private Stereo stereo; private Projector projector; private Screen screen; private DVDPlayer dVDPlayer; //构造器 public HomeTheaterFacade() { super(); this.theaterLight = TheaterLight.getInstance(); this.popcorn = Popcorn.getInstance(); this.stereo = Stereo.getInstance(); this.projector = Projector.getInstance(); this.screen = Screen.getInstance(); this.dVDPlayer = DVDPlayer.getinstance(); } //操作分成四步 public void ready() { popcorn.on(); popcorn.pop(); screen.down(); projector.on(); stereo.on(); dVDPlayer.on(); theaterLight.dim(); } //准备好了开始播放 public void play() { dVDPlayer.play(); } public void pause() { dVDPlayer.pause(); } public void end() { popcorn.off(); theaterLight.bright(); screen.up(); projector.off(); stereo.off(); dVDPlayer.off(); } }
DVDPlayer子系统大同小异就放一个
public class DVDPlayer { //使用单例模式,使用饿汉式 private static DVDPlayer instance=new DVDPlayer(); public static DVDPlayer getinstance() { return instance; } public void on() { System.out.println("dvd on"); } public void off() { System.out.println(" dvd off "); } public void play() { System.out.println(" dvd is playing "); } //.... public void pause() { System.out.println(" dvd pause .."); } }
Client
public class Client{ public static void main(String[] args) { HomeTheaterFacade facade=new HomeTheaterFacade(); facade.ready(); facade.play(); facade.end(); } }
-
享元模式
内部状态与外部状态
-
享元模式提出了两个要求:细粒度和共享对象。这里就涉及到内部状态和外部状态了,即将对象的信息分为两个部分:内部状态和外部状态
-
内部状态指对象共享出来的信息,存储在享元对象内部且不会随环境的改变而改变。
外部状态指对象得以依赖的一个标记,是随环境改变而改变的、不可共享的状态。
介绍与原理类图
-
介绍
- 享元模式(Flyweight Pattern) 也叫蝇量模式,运用共享技术有效地支持大量细粒度的对象。
- 常用于系统底层开发,解决系统的性能问题。像数据库连接池,里面都是创建好的连接对象,在
这些连接对象中有我们需要的则直接拿来用,避免重新创建,如果没有我们需要的,则创建一个放入连接池,而不用每次都创建新的对象从数据库获得
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xCkB8UXr-1641114981193)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20211229215859055.png)] - 享元模式能够解决重复对象的内存浪费的问题,当系统中有大量相似对象,需要缓冲池时。不需
总是创建新对象,可以从缓冲池里拿。这样可以降低系统内存,同时提高效率。
-
原理类图
- FlyWeight 是抽象的享元角色, 他是产品的抽象类, 同时定义出对象的外部状态和内部状态的接口或实现。
- ConcreteFlyWeight 是具体的享元角色,是具体的产品类,实现抽象角色定义相关业务
- UnSharedConcreteFlyWeight 是不可共享的角色,一般不会出现在享元工厂。
- FlyweightFactory 享元工厂类,用于构建一个池容器(集合),同时提供从池中获取对象方法。
实例讲解
-
案例介绍
-
传统方案解决网站展现项目
-
方式介绍
-
问题
需要的网站结构相似度很高,而且都不是高访问量网站,如果分成多个虚拟空间来处理,相当于一个相同网站的实例对象很多,造成服务器的资源浪费
-
解决思路:
整合到一个网站中,共享其相关的代码和数据,对于硬盘、内存、CPU、数据库空间等服务器资源都可以达成共享,减少服务器资源—>享元模式
-
-
享元模式解决问题:
-
UML
-
代码:
ConcreteWebSite:是共享的,内部状态 每个网页相似的部分
public class ConcreteWebSite extends WebSite{ private String type=""; public ConcreteWebSite(String type) { this.type = type; } @Override public void use() { // TODO Auto-generated method stub System.out.println("完站的发布形式为"+type); } }
Website
public abstract class WebSite { public abstract void use(); }
WebSiteFactory
//网站工厂类 public class WebSiteFactory { //集合,充当池的作用 private HashMap<String , ConcreteWebSite> pool=new HashMap<>(); //根据网站的类型,返回一个网站,如果没有就创建一个放入池中,并返回 public WebSite getWebSiteCategory(String type) { if(!pool.containsKey(type)) { //就创建一个网站,并放入到池中 pool.put(type, new ConcreteWebSite(type)); } return (WebSite)pool.get(type); } //获取网站分类总数(池中有多少网络类型) public int getWebSiteCount() { return pool.size(); } }
Client
public class Client { public static void main(String[] args) { // TODO Auto-generated method stub // 创建一个工厂类 WebSiteFactory factory = new WebSiteFactory(); // 客户要一个以新闻形式发布的网站 WebSite webSite1 = factory.getWebSiteCategory("新闻"); webSite1.use(new User("tom")); // 客户要一个以博客形式发布的网站 WebSite webSite2 = factory.getWebSiteCategory("博客"); webSite2.use(new User("jack")); // 客户要一个以博客形式发布的网站 WebSite webSite3 = factory.getWebSiteCategory("博客"); webSite3.use(new User("smith")); // 客户要一个以博客形式发布的网站 WebSite webSite4 = factory.getWebSiteCategory("博客"); webSite4.use(new User("king")); System.out.println("网站的分类共=" + factory.getWebSiteCount()); } }
-
代理模式
- 代理模式:为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。
- 被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的对象
- 代理模式有不同的形式, 主要有三种 静态代理、动态代理 (JDK代理、接口代理)和 Cglib代理 (可以在内存动态的创建对象,而不需要实现接口, 他是属于动态代理的范畴) 。
静态代理
-
介绍
静态代理在使用时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同父类 。
-
实例讲解:
-
案例:
-
UML
-
代码:
ITeacherDao
//接口 public interface ITeacherDao { void teach(); }
TeacherDao
public class TeacherDao implements ITeacherDao{ @Override public void teach() { // TODO Auto-generated method stub System.out.println("老师授课中"); } }
TeacherDaoProxy
public class TeacherDaoProxy implements ITeacherDao{ private ITeacherDao target;//目标对象生病老师,通过接口来聚合 //参数传TeacherDAO public TeacherDaoProxy(ITeacherDao target) { super(); this.target = target; } @Override public void teach() { // TODO Auto-generated method stub System.out.println("代理开始 完成某些操作"); target.teach(); System.out.println("代理结束"); } }
Client
public class Client { public static void main(String[] args) { //创建目标对象(被代理对象) TeacherDao dao=new TeacherDao(); TeacherDaoProxy daoProxy=new TeacherDaoProxy(dao); //通过代理对象,调用被代理对象方法 //执行的是代理对象的方法,代理对象再调用目标对象方法 daoProxy.teach(); } }
-
-
优缺点:
- 优点:在不修改目标对象的功能前提下, 能通过代理对象对目标功能扩展
- 缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,一旦接口增加方法,目标对象与代理对象都要维护。
动态代理
-
介绍
- 代理对象,不需要实现接口,但是目标对象要实现接口,否则不能用动态代理
- 代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象
- 动态代理也叫做:JDK代理、接口代理
-
实例讲解(案例同上)
-
UML
-
代码:
ITeacherDao
public interface ITeacherDao { void teach(); void sayHello(String name); }
TeacherDao
public class TeacherDao implements ITeacherDao{ @Override public void teach() { // TODO Auto-generated method stub System.out.println("老师正在授课中.................."); } @Override public void sayHello(String name) { // TODO Auto-generated method stub System.out.println(name+"说:你好同学们"); } }
Client
public class Client { public static void main(String[] args) { //创建目标对象 ITeacherDao target=new TeacherDao(); //给目标对象创建代理对象,可以转成ITeacherDao ITeacherDao proxyInstance=(ITeacherDao) new ProxyFactory(target).getProxyInstance(); //ProxyInstanceclass com.sun.proxy.$Proxy0 内存中动态生成了代理对象 System.out.println("ProxyInstance"+proxyInstance.getClass()); //通过代理对象,调用目标对象方法 proxyInstance.teach(); proxyInstance.sayHello("宝哥"); } }
ProxyFactory
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class ProxyFactory { // 维护一个目标对象 private Object target; public ProxyFactory(Object target) { this.target = target; } // 给目标对象生成代理对象 public Object getProxyInstance() { // 说明 /** * public static Object newProxyInstance(ClassLoader loader, Class<?>[] * interfaces, InvocationHandler h) 1.ClassLoader * loader:指定当前目标对象使用的类加载器,获取加载器的方法固定 * * 2. Class<?>[] interfaces:目标对象实现的额接口类型,使用泛型方法确认类型 * * 3.InvocationHandler h:事情处理,会触发事情处理器方法,会把当前执行的目标对象方法作为参数传入 */ return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // TODO Auto-generated method stub System.out.println("JDK代理开始"); // 反射机制调用目标对象的方法 Object returnVal = method.invoke(target, args); System.out.println("JDK代理结束"); return returnVal; } }); } }
Method method:是将目标对象的方法作为一个对象
Object[] args:存的是目标对象方法的参数
以上两参数是随着调用目标对象的方法的变化而变化
-
代理模式(Proxy)的变体
行为型模式
模版方法模式
介绍与原理类图
-
介绍
-
模板方法模式(Template Method Pattern),又叫模板模式(Template Pattern),在一个抽象类公开定义了执行它的方法的模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。
-
简单说,模板方法模式 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构,就可以重定义该算法的某些特定步骤
-
-
原理类图
-
AbstractClass 抽象类, 类中实现了模板方法(template),定义了算法的骨架,具体子类需要去实现 其它的抽象方法operationr2,3,4
-
ConcreteClass 实现抽象方法operationr2,3,4, 以完成算法中特点子类的步骤
-
实例讲解
-
案例介绍
-
方法一:
-
UML
-
代码
SoyaMilk
public abstract class SoyaMilk { //模板方法, make , 模板方法可以做成final , 不让子类去覆盖. final void make() { select(); addCondiments(); soak(); beat(); } //选材料 void select() { System.out.println("第一步:选择好的新鲜黄豆 "); } //添加不同的配料, 抽象方法, 子类具体实现 abstract void addCondiments(); //浸泡 void soak() { System.out.println("第三步, 黄豆和配料开始浸泡, 需要3小时 "); } void beat() { System.out.println("第四步:黄豆和配料放到豆浆机去打碎 "); } }
PeanutSoyaMilk
public class PeanutSoyaMilk extends SoyaMilk { @Override void addCondiments() { // TODO Auto-generated method stub System.out.println(" 加入上好的花生 "); } }
client
public class Client { public static void main(String[] args) { // TODO Auto-generated method stub //制作红豆豆浆 System.out.println("----制作红豆豆浆----"); SoyaMilk redBeanSoyaMilk = new RedBeanSoyaMilk(); redBeanSoyaMilk.make(); System.out.println("----制作花生豆浆----"); SoyaMilk peanutSoyaMilk = new PeanutSoyaMilk(); peanutSoyaMilk.make(); } }
-
-
改进:钩子方法
-
介绍
在模板方法模式的父类中,我们可以定义一个方法,它默认不做任何事,子类可以视情况要不要覆盖它,该方法称为“钩子”。
-
代码
SoyaMilk加一个钩子方法
public abstract class SoyaMilk { //模板方法, make , 模板方法可以做成final , 不让子类去覆盖. final void make() { select(); if(customerWantCondiments()) { addCondiments(); } soak(); beat(); } //选材料 void select() { System.out.println("第一步:选择好的新鲜黄豆 "); } //添加不同的配料, 抽象方法, 子类具体实现 abstract void addCondiments(); //浸泡 void soak() { System.out.println("第三步, 黄豆和配料开始浸泡, 需要3小时 "); } void beat() { System.out.println("第四步:黄豆和配料放到豆浆机去打碎 "); } //钩子方法,决定是否需要添加配料 boolean customerWantCondiments() { return true; } }
增加一个纯豆浆类:*PureSoyaMilk *继承钩子更改钩子结果
public class PureSoyaMilk extends SoyaMilk{ @Override void addCondiments() { // TODO Auto-generated method stub //空实现 } @Override boolean customerWantCondiments() { // TODO Auto-generated method stub return false; } }
-
模板方法模式的注意事项和细节
命令模式
基本介绍和原理类图
-
介绍
-
原理类图:
- Invoker 是调用者角色
- Command: 是命令角色,需要执行的所有命令都在这里,可以是接口或抽象类
- Receiver: 接受者角色,知道如何实施和执行一个请求相关的操作
- ConcreteCommand: 将一个接受者对象与一个动作绑定,调用接受者相应的操作,实现execute
实例讲解
-
案例介绍
-
UML
-
代码
RemoteController
public class RemoteController { //开按钮的命令数组 Command oncommand[]; Command offcommand[]; //执行撤销命令,记录上一次操作才能撤销 Command undoCommand; public RemoteController() { oncommand=new Command[5]; offcommand=new Command[5]; for(int i=0;i<5;i++) { oncommand[i]=new NoCommand(); offcommand[i]=new NoCommand(); } } //给我们的按钮设置你需要的命令 public void setCommand(int num,Command onCommand,Command offCommand) { oncommand[num]=onCommand; offcommand[num]=offCommand; } //按下开按钮 public void onButtonWasPushed(int num) { //找到按下的开的按钮并调用方法 oncommand[num].excute(); //记录这次操作用于撤销 undoCommand=oncommand[num]; } //按下关按钮 public void offButtonWasPushed(int num) { //找到按下的关的按钮并调用方法 offcommand[num].excute(); undoCommand=offcommand[num]; } //按下撤销按钮 public void undoButtonWasPushed() { undoCommand.undo(); } }
Command
//创建命令接口 public interface Command { //执行动作(操作) public void excute(); //撤销动作(操作) public void undo(); }
LightOnCommand
public class LightOnCommand implements Command{ //聚合LightReceiver LightReceiver light; public LightOnCommand(LightReceiver light) { super(); this.light = light; } @Override public void excute() { // TODO Auto-generated method stub //调用接受者的方法 light.on(); } @Override public void undo() { // TODO Auto-generated method stub light.off(); } }
LightOffCommand
public class LightOffCommand implements Command{ //聚合LightReceiver LightReceiver light; public LightOffCommand(LightReceiver light) { super(); this.light = light; } @Override public void excute() { // TODO Auto-generated method stub //调用接受者的方法 light.off(); } @Override public void undo() { // TODO Auto-generated method stub light.on(); } }
NoCommand
public class NoCommand implements Command{ @Override public void excute() { // TODO Auto-generated method stub } @Override public void undo() { // TODO Auto-generated method stub } }
Client
public class Client { public static void main(String[] args) { //使用命令模式,完成通过遥控器,对电灯的操作 //创建电灯的对象(接受者) LightReceiver lightReceiver=new LightReceiver(); //创建电灯相关的开关命令 LightOnCommand lightOnCommand=new LightOnCommand(lightReceiver); LightOffCommand lightOffCommand=new LightOffCommand(lightReceiver); //需要一个遥控器 RemoteController remoteController=new RemoteController(); //给我们的遥控器设置命令,比如no=0是电灯的开和关的操作 remoteController.setCommand(0, lightOnCommand, lightOffCommand); System.out.println("-----------------按下灯的开按钮---------------------"); remoteController.onButtonWasPushed(0); System.out.println("-----------------按下灯的关按钮---------------------"); remoteController.offButtonWasPushed(0); System.out.println("-----------------按下灯的撤销按钮---------------------"); remoteController.undoButtonWasPushed(); } }
LightReceiver
public class LightReceiver { public void on() { System.out.println("电灯打开了"); } public void off() { System.out.println("电灯关闭了"); } }
访问者模式
介绍与原理类图
-
介绍
-
访问者模式(Visitor Pattern),封装一些作用于某种数据结构的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。
-
主要将数据结构与数据操作分离,解决数据结构和操作耦合性问题
-
访问者模式的基本工作原理是:在被访问的类里面加一个对外提供接待访问者的接口。
-
访问者模式主要应用场景是:需要对一个对象结构中的对象进行很多不同操作(这些操作彼此没有关联),同时需要避免让这些操作"污染"这些对象的类,可以选用访问者模式解决
-
-
原理类图
- Visitor 是抽象访问者,为该对象结构中的ConcreteElement的每一个类声明一个visit操作
- ConcreteVisitor :是一个具体的访问值 实现每个有Visitor 声明的操作,是每个操作实现的部分.
- ObjectStructure 能枚举它的元素, 可以提供一个高层的接口,用来允许访问者访问元素
- Element 定义一个accept 方法,接收一个访问者对象
- ConcreteElement 为具体元素,实现了accept 方法
实例讲解
-
案例介绍
将观众分为男人和女人,对歌手进行测评,当看完某个歌手表演后,得到他们对该歌手不同的评价(评价 有不同的种类,比如 成功、失败 等)
-
传统方式及问题
传统方式的问题分析
- 如果系统比较小,还是ok的,但是考虑系统增加越来越多新的功能时,对代码改动较大,违反了ocp原则, 不利于维护。
- 扩展性不好,比如 增加了 新的人员类型,或者管理方法,都不好做
-
访问者模式解决问题
-
UML
-
代码
Person
public abstract class Person { //提供一个方法,让访问者可以访问 public abstract void accept(Action action); }
Man
public class Man extends Person{ @Override public void accept(Action action) { // TODO Auto-generated method stub action.getManResult(this); } }
Woman
public class Woman extends Person{ //说明 //1. 这里我们使用到了双分派, 即首先在客户端程序中,将具体状态作为参数传递Woman中(第一次分派) //2. 然后Woman 类调用作为参数的 "具体方法" 中方法getWomanResult, 同时将自己(this)作为参数 // 传入,完成第二次的分派 @Override public void accept(Action action) { // TODO Auto-generated method stub action.getWomanResult(this); } }
Action
public abstract class Action { //得到男性的测评结果 public abstract void getManResult(Man man); //得到女性的测评结果 public abstract void getWomanResult(Woman woman); }
Success
public class Success extends Action{ @Override public void getManResult(Man man) { System.out.println("男观众表示歌手成功"); } @Override public void getWomanResult(Woman woman) { System.out.println("女观众表示歌手成功"); } }
Fail
public class Fail extends Action{ @Override public void getManResult(Man man) { System.out.println("男观众表示歌手失败"); } @Override public void getWomanResult(Woman woman) { System.out.println("女观众表示歌手失败"); } }
ObjectStructure
public class ObjectStructure { private List<Person> persons=new ArrayList<Person>(); //增加到list public void addPerson(Person p) { persons.add(p); } //从list中移出 public void removePerson(Person p) { persons.remove(p); } //显示 action给什么人们的评价就是什么 public void show(Action action) { for(Person p:persons) { p.accept(action); } } }
Client
public class Client { public static void main(String[] args) { // TODO Auto-generated method stub ObjectStructure objectStructure=new ObjectStructure(); objectStructure.addPerson(new Man()); objectStructure.addPerson(new Woman()); //成功 Success success=new Success(); objectStructure.show(success); } }
-
双分派改进
-
-
注意事项和细节
迭代器模式
介绍与原理类图
-
介绍
- 如果我们的集合元素是用不同的方式实现的,有数组,还有java的集合类,或者还有其他方式,当客户端要遍历这些集合元素的时候就要使用多种遍历方式,而且还会暴露元素的内部结构,可以考虑使用迭代器模式解决。
- 迭代器模式,提供一种遍历集合元素的统一接口,用一致的方法遍历集合元素,不需要知道集合对象的底层表示,即:不暴露其内部的结构
-
原理类图
- Iterator :迭代器接口,是系统提供,含义hasNext, next, remove 。
- ConcreteIterator : 具体的迭代器类,管理迭代。
- Aggregate :一个统一的聚合接口,将客户端(client)和具体聚合(ConcreteAggregate)解耦。
- ConcreteAggreage:具体的聚合(Element)持有对象集合,并提供一个方法,返回一个迭代器,该迭代器可以正确遍历集合
- client:客户端,依赖Aggragate和Iterator的子类
实例讲解
-
案例
编写程序展示一个学校院系结构:需求是这样,要在一个页面中展示出学校的院系
组成,一个学校有多个学院,一个学院有多个系。
-
UML
使输出与具体的遍历方法解耦
-
代码
ComputerCollegeIterator
public class ComputerCollegeIterator implements Iterator { // 这里我们需要知道Department是以怎样的方式存放 Department[] departments; // 遍历位置 int position = 0; public ComputerCollegeIterator(Department[] department) { super(); this.departments = department; } @Override public boolean hasNext() { // TODO Auto-generated method stub if (position >= departments.length || departments[position] == null) { return false; }else { return true; } } @Override public Object next() { // TODO Auto-generated method stub Department department=departments[position]; position+=1; return department; } //删除的方法默认空实现 public void remove() { } }
InfoColleageIterator
//集合遍历 public class InfoColleageIterator implements Iterator{ List<Department> departments; int index=-1;//索引 public InfoColleageIterator(List<Department> departments) { super(); this.departments = departments; } //判断list中还有没有下一个元素 @Override public boolean hasNext() { // TODO Auto-generated method stub if(index>=departments.size()-1) { return false; }else { index+=1; return true; } } @Override public Object next() { // TODO Auto-generated method stub return departments.get(index); } //空实现remove public void remove() { } }
College
public interface College { public String getName(); //增加系的方法 public void addDepartment(String name,String intro); //返回一个迭代器,遍历 public Iterator createIterator(); }
ComputerCollege
public class ComputerCollege implements College{ Department []departments; int numofDepartment=0;//保存当前数组的对象个数 public ComputerCollege() { // TODO Auto-generated constructor stub departments=new Department[5]; addDepartment("java专业", "java专业"); addDepartment("PHP专业", "PHP专业"); addDepartment("大数据专业", "大数据专业"); } @Override public String getName() { // TODO Auto-generated method stub return "计算机学院"; } @Override public void addDepartment(String name, String intro) { // TODO Auto-generated method stub Department department = new Department(name, intro); departments[numofDepartment]=department; numofDepartment+=1; } //依赖ComputerCollegeIterator 返回该迭代器 @Override public Iterator createIterator() { // TODO Auto-generated method stub return new ComputerCollegeIterator(departments); } }
InfoCollege
public class InfoCollege implements College{ List<Department> departments; public InfoCollege() { departments=new ArrayList<Department>(); addDepartment("信息安全专业", "信息安全专业"); addDepartment("网络安全专业", "网络安全专业"); addDepartment("服务器安全专业", "服务器安全专业"); } @Override public String getName() { // TODO Auto-generated method stub return "信息工程学院"; } @Override public void addDepartment(String name, String intro) { // TODO Auto-generated method stub Department department=new Department(name, intro); departments.add(department); } @Override public Iterator createIterator() { // TODO Auto-generated method stub return new InfoColleageIterator(departments); } }
Department
public class Department { private String name; private String intro; public Department(String name, String intro) { super(); this.name = name; this.intro = intro; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getIntro() { return intro; } public void setIntro(String intro) { this.intro = intro; } }
OutputImpl
public class OutputImpl { //学院集合 List<College> colleges; public OutputImpl(List<College> colleges) { super(); this.colleges = colleges; } //遍历所有学院,然后调用printDepartment输出各个学院的系 public void printCollege() { //从colleges取出所有学院 list本身实现了Iterator Iterator<College> iterator = colleges.iterator(); while(iterator.hasNext()) { College college = iterator.next(); System.out.println("======"+college.getName()+"======="); //使用当前学院的迭代器遍历系 printDepartment(college.createIterator()); } } //输出 学院输出系 public void printDepartment(Iterator iterator) { while(iterator.hasNext()) { Department d=(Department) iterator.next(); System.out.println(d.getName()); } } }
Client
public class Client { public static void main(String[] args) { //创建学院 List<College> collegeList = new ArrayList<College>(); ComputerCollege computerCollege=new ComputerCollege(); InfoCollege infoCollege = new InfoCollege(); collegeList.add(computerCollege); collegeList.add(infoCollege); OutputImpl impl=new OutputImpl(collegeList); impl.printCollege(); } }
迭代器模式的注意事项和细节
观察者模式
观察者模式原理
观察者模式原理
观察者模式类似订牛奶业务:
- 奶站/气象局:Subject
- 用户/第三方网站:Observer
Subject
-
Subject:登记注册、移除和通知
-
registerObserver 注册
-
removeObserver 移除
-
notifyObservers() 通知所有的注册的用户,根据不同需求,可以是更新数据,让用
户来取,也可能是实施推送,看具体需求定
Observer:接收输入
观察者模式:对象之间多对一依赖的一种设计方案,被依赖的对象为Subject,依赖的对象为Observer,Subject通知Observer变化,比如这里的奶站是Subject,是1的一方。用户时Observer,是多的一方。
实例讲解
-
案例说明
-
解决方案一:普通方案
-
设计WeatherData类
-
通过getXxx方法,可以让第三方接入,并得到相关信息。
-
当数据有更新时,气象站通过调用dataChange() 去更新数据,当第三方再次获取时,就能得到最新数据,当然也可以推送(下图)。
-
-
代码
CurrentConditions
/** * 显示当前天气情况(可以理解成是气象站自己的网站) * @author Administrator * */ public class CurrentConditions { // 温度,气压,湿度 private float temperature; private float pressure; private float humidity; //更新 天气情况,是由 WeatherData 来调用,我使用推送模式 public void update(float temperature, float pressure, float humidity) { this.temperature = temperature; this.pressure = pressure; this.humidity = humidity; display(); } //显示 public void display() { System.out.println("***Today mTemperature: " + temperature + "***"); System.out.println("***Today mPressure: " + pressure + "***"); System.out.println("***Today mHumidity: " + humidity + "***"); } }
WeatherData
/** * 类是核心 * 1. 包含最新的天气情况信息 * 2. 含有 CurrentConditions 对象 * 3. 当数据有更新时,就主动的调用 CurrentConditions对象update方法(含 display), 这样他们(接入方)就看到最新的信息 * @author Administrator * */ public class WeatherData { private float temperatrue; private float pressure; private float humidity; private CurrentConditions currentConditions; //加入新的第三方 public WeatherData(CurrentConditions currentConditions) { this.currentConditions = currentConditions; } public float getTemperature() { return temperatrue; } public float getPressure() { return pressure; } public float getHumidity() { return humidity; } public void dataChange() { //调用 接入方的 update currentConditions.update(getTemperature(), getPressure(), getHumidity()); } //当数据有更新时,就调用 setData public void setData(float temperature, float pressure, float humidity) { this.temperatrue = temperature; this.pressure = pressure; this.humidity = humidity; //调用dataChange, 将最新的信息 推送给 接入方 currentConditions dataChange(); } }
client
public class Client { public static void main(String[] args) { //创建接入方 currentConditions CurrentConditions currentConditions = new CurrentConditions(); //创建 WeatherData 并将 接入方 currentConditions 传递到 WeatherData中 WeatherData weatherData = new WeatherData(currentConditions); //更新天气情况 weatherData.setData(30, 150, 40); //天气情况变化 System.out.println("============天气情况变化============="); weatherData.setData(40, 160, 20); } }
-
问题分析:
- 其他第三方接入气象站获取数据的问题
- 无法在运行时动态的添加第三方 (新浪网站)
- 违反ocp原则
-
-
解决方案二:改进方案
-
UML
-
代码:
Observe
//观察者接口 public interface Observe { public void update(float temperature,float pressure,float humidty); }
CurrentConditions
public class CurrentConditions implements Observe{ // 温度,气压,湿度 private float temperature; private float pressure; private float humidity; // 更新 天气情况,是由 WeatherData 来调用,我使用推送模式 public void update(float temperature, float pressure, float humidity) { this.temperature = temperature; this.pressure = pressure; this.humidity = humidity; display(); } // 显示 public void display() { System.out.println("***Today mTemperature: " + temperature + "***"); System.out.println("***Today mPressure: " + pressure + "***"); System.out.println("***Today mHumidity: " + humidity + "***"); } }
BaiduSite
public class BaiduSite implements Observe { // 温度,气压,湿度 private float temperature; private float pressure; private float humidity; // 更新 天气情况,是由 WeatherData 来调用,我使用推送模式 public void update(float temperature, float pressure, float humidity) { this.temperature = temperature; this.pressure = pressure; this.humidity = humidity; display(); } // 显示 public void display() { System.out.println("===百度网站===="); System.out.println("***百度网站 气温 : " + temperature + "***"); System.out.println("***百度网站 气压: " + pressure + "***"); System.out.println("***百度网站 湿度: " + humidity + "***"); } }
Subject
public interface Subject { public void registerObserve(Observe observe); public void removeObserve(Observe observe); public void notifyObserve(); }
WeatherData
/** * 类是核心 1. 包含最新的天气情况信息 2. 含有 观察者集合,使用ArrayList管理 3. 当数据有更新时,就主动的调用 * CurrentConditions对象update方法(含 display), 这样通知所有接入方就看到最新的信息 * * @author Administrator * */ public class WeatherData implements Subject { private float temperatrue; private float pressure; private float humidity; // 观察者集合 private ArrayList<Observe> observes; // 加入新的第三方 public WeatherData() { observes = new ArrayList<Observe>(); } public float getTemperature() { return temperatrue; } public float getPressure() { return pressure; } public float getHumidity() { return humidity; } public void dataChange() { notifyObserve(); } // 当数据有更新时,就调用 setData public void setData(float temperature, float pressure, float humidity) { this.temperatrue = temperature; this.pressure = pressure; this.humidity = humidity; // 调用dataChange, 将最新的信息 推送给 接入方 currentConditions dataChange(); } @Override public void registerObserve(Observe observe) { // TODO Auto-generated method stub observes.add(observe); } @Override public void removeObserve(Observe observe) { // TODO Auto-generated method stub if (observes.contains(observe)) { observes.remove(observe); } } // 遍历所有观察者并通知 @Override public void notifyObserve() { // TODO Auto-generated method stub for(int i=0;i<observes.size();i++) { observes.get(i).update(getTemperature(), getPressure(), getHumidity()); } } }
Client
public class Client { public static void main(String[] args) { //创建WeatherData WeatherData weatherData=new WeatherData(); //创建观察者 CurrentConditions currentConditions=new CurrentConditions(); BaiduSite baiduSite=new BaiduSite(); //注册到weatherData weatherData.registerObserve(currentConditions); weatherData.registerObserve(baiduSite); //测试 System.out.println("通知各个网站,信息更新"); weatherData.setData(10f, 100f, 30.3f); } }
-
中介者模式
介绍与原理类图
-
介绍
- 中介者模式(Mediator Pattern),用一个中介对象来封装一系列的对象交互。中介者使各个对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
- 比如MVC模式,C(Controller控制器)是M(Model模型)和V(View视图)的中介者,在前后端交互时起到了中间人的作用
-
原理类图
- Mediator 就是抽象中介者,定义了同事对象到中介者对象的接口
- Colleague 是抽象同事类
- ConcreteMediator 具体的中介者对象, 实现抽象方法, 他需要知道所有的具体的同事类,即以一个集合来管理HashMap,并接受某个对象消息,完成相应的任务
- ConcreteColleague 具体的同事类,会有很多,每个同事只知道自己行为,而不了解其他同事的行为(方法),但是他们都依赖中介者对象
实例讲解
-
案例介绍
-
传统方案:
-
类图
-
问题分析
- 当各电器对象有多种状态改变时,相互之间的调用关系会比较复杂
- 各个电器对象彼此联系,你中有我,我中有你,不利于松耦合.
- 各个电器对象之间所传递的消息(参数),容易混乱
- 当系统增加一个新的电器对象时,或者执行流程改变时,代码的可维护性、扩展性
都不理想 —> 考虑中介者模式
-
-
中介者模式解决
-
UML
中介者模式-只能家庭操作流程
- 场景ConcreMediator对象
- 创建各个同事类对象,比如Alarm,coffeeMachine,TV…
- 在创建同事对象的时候,直接通过构造器,加入到colleagueMap中去
- 同事类对象,可以调用sendMessage(),最终会去调用getMassage()方法
- getMassage()会根据接受到的同事对象发出的消息来协调调用其他的同事对象完成任务。
-
代码:
Mediator
public abstract class Mediator { //将给中介者对象,加入到集合中 public abstract void Register(String colleagueName, Colleague colleague); //接收消息, 具体的同事对象发出 public abstract void GetMessage(int stateChange, String colleagueName); public abstract void SendMessage(); }
ConcreteMediator
//具体的中介者类 public class ConcreteMediator extends Mediator { //集合,放入所有的同事对象 private HashMap<String, Colleague> colleagueMap; private HashMap<String, String> interMap; public ConcreteMediator() { colleagueMap = new HashMap<String, Colleague>(); interMap = new HashMap<String, String>(); } @Override public void Register(String colleagueName, Colleague colleague) { // TODO Auto-generated method stub colleagueMap.put(colleagueName, colleague); // TODO Auto-generated method stub if (colleague instanceof Alarm) { interMap.put("Alarm", colleagueName); } else if (colleague instanceof CoffeeMachine) { interMap.put("CoffeeMachine", colleagueName); } else if (colleague instanceof TV) { interMap.put("TV", colleagueName); } else if (colleague instanceof Curtains) { interMap.put("Curtains", colleagueName); } } //具体中介者的核心方法 //1. 根据得到消息,完成对应任务 //2. 中介者在这个方法,协调各个具体的同事对象,完成任务 @Override public void GetMessage(int stateChange, String colleagueName) { // TODO Auto-generated method stub //处理闹钟发出的消息 if (colleagueMap.get(colleagueName) instanceof Alarm) { if (stateChange == 0) { ((CoffeeMachine) (colleagueMap.get(interMap .get("CoffeeMachine")))).StartCoffee(); ((TV) (colleagueMap.get(interMap.get("TV")))).StartTv(); } else if (stateChange == 1) { ((TV) (colleagueMap.get(interMap.get("TV")))).StopTv(); } } else if (colleagueMap.get(colleagueName) instanceof CoffeeMachine) { ((Curtains) (colleagueMap.get(interMap.get("Curtains")))) .UpCurtains(); } else if (colleagueMap.get(colleagueName) instanceof TV) {//如果TV发现消息 } else if (colleagueMap.get(colleagueName) instanceof Curtains) { //如果是以窗帘发出的消息,这里处理... } } @Override public void SendMessage() { // TODO Auto-generated method stub } }
Colleague
//同事抽象类 public abstract class Colleague { private Mediator mediator; public String name; public Colleague(Mediator mediator, String name) { this.mediator = mediator; this.name = name; } public Mediator GetMediator() { return this.mediator; } public abstract void SendMessage(int stateChange); }
Alarm
//具体的同事类 public class Alarm extends Colleague { //构造器 public Alarm(Mediator mediator, String name) { super(mediator, name); // TODO Auto-generated constructor stub //在创建Alarm 同事对象时,将自己放入到ConcreteMediator 对象中[集合] mediator.Register(name, this); } public void SendAlarm(int stateChange) { SendMessage(stateChange); } @Override public void SendMessage(int stateChange) { // TODO Auto-generated method stub //调用的中介者对象的getMessage this.GetMediator().GetMessage(stateChange, this.name); } }
CoffeeMachine
public class CoffeeMachine extends Colleague { public CoffeeMachine(Mediator mediator, String name) { super(mediator, name); // TODO Auto-generated constructor stub mediator.Register(name, this); } @Override public void SendMessage(int stateChange) { // TODO Auto-generated method stub this.GetMediator().GetMessage(stateChange, this.name); } public void StartCoffee() { System.out.println("It's time to startcoffee!"); } public void FinishCoffee() { System.out.println("After 5 minutes!"); System.out.println("Coffee is ok!"); SendMessage(0); } }
Curtains
public class Curtains extends Colleague { public Curtains(Mediator mediator, String name) { super(mediator, name); // TODO Auto-generated constructor stub mediator.Register(name, this); } @Override public void SendMessage(int stateChange) { // TODO Auto-generated method stub this.GetMediator().GetMessage(stateChange, this.name); } public void UpCurtains() { System.out.println("I am holding Up Curtains!"); } }
TV
public class TV extends Colleague { public TV(Mediator mediator, String name) { super(mediator, name); // TODO Auto-generated constructor stub mediator.Register(name, this); } @Override public void SendMessage(int stateChange) { // TODO Auto-generated method stub this.GetMediator().GetMessage(stateChange, this.name); } public void StartTv() { // TODO Auto-generated method stub System.out.println("It's time to StartTv!"); } public void StopTv() { // TODO Auto-generated method stub System.out.println("StopTv!"); } }
ClientText
public class ClientTest { public static void main(String[] args) { //创建一个中介者对象 Mediator mediator = new ConcreteMediator(); //创建Alarm 并且加入到 ConcreteMediator 对象的HashMap Alarm alarm = new Alarm(mediator, "alarm"); //创建了CoffeeMachine 对象,并 且加入到 ConcreteMediator 对象的HashMap CoffeeMachine coffeeMachine = new CoffeeMachine(mediator, "coffeeMachine"); //创建 Curtains , 并 且加入到 ConcreteMediator 对象的HashMap Curtains curtains = new Curtains(mediator, "curtains"); TV tV = new TV(mediator, "TV"); //让闹钟发出消息 alarm.SendAlarm(0); coffeeMachine.FinishCoffee(); alarm.SendAlarm(1); } }
-
备忘录模式
介绍与原理类图
-
介绍
- 备忘录模式(Memento Pattern)在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态
- 可以这里理解备忘录模式:现实生活中的备忘录是用来记录某些要去做的事情,或者是记录已经达成的共同意见的事情,以防忘记了。而在软件层面,备忘录模式有着相同的含义,备忘录对象主要用来记录一个对象的某种状态,或者某些数据,当要做回退时,可以从备忘录对象里获取原来的数据进行恢复操作
-
原理类图
-
originator : 对象(需要保存状态的对象)
-
Memento : 备忘录对象,负责保存好记录,即Originator内部状态
-
Caretaker: 守护者对象,负责保存多个备忘录对象, 使用集合管理,提高效率
说明:如果希望保存多个originator对象的不同时间的状态,也可以,只需要
要 HashMap <String, 集合>
-
-
原理代码
-
Originator
public class Originator { private String state; public String getState() { return state; } public void setState(String state) { this.state = state; } //编写一个方法可以保存一个状态对象 Memento,方法返回Memento对象 public Memento saveStateMemento() { return new Memento(state); } //通过备忘录对象恢复状态 public void getStateFromMemento(Memento memento) { state=memento.getState(); } }
-
Memento
public class Memento { private String state; public Memento(String state) { super(); this.state = state; } public String getState() { return state; } }
-
Caretake
public class Caretaker { private List<Memento> mementos=new ArrayList<Memento>(); public void add(Memento memento) { mementos.add(memento); } public Memento getMemento(int index) { return mementos.get(index); } public void remove(Memento memento) { mementos.remove(memento); } }
-
Client
public class Client { public static void main(String[] args) { Originator originator=new Originator(); Caretaker caretaker = new Caretaker(); originator.setState("状态1:攻击力为100"); //保存当前状态 caretaker.add(originator.saveStateMemento()); originator.setState("状态2:攻击力为80"); caretaker.add(originator.saveStateMemento()); originator.setState("状态3:攻击力为60"); caretaker.add(originator.saveStateMemento()); System.out.println("当前状态是="+originator.getState()); //希望恢复到状态1 originator.getStateFromMemento(caretaker.getMemento(0)); System.out.println("当前状态是="+originator.getState()); } }
-
实例讲解
-
案例说明
游戏角色有攻击力和防御力,在大战Boss前保存自身的状态(攻击力和防御力),当大战Boss后攻击力和防御力下降,从备忘录对象恢复到大战前的状态
-
传统方案解决
-
一个对象,就对应一个保存对象状态的对象, 这样当我们游戏的对象很多时,不利于管理,开销也很大。
-
传统的方式是简单地做备份,new出另外一个对象出来,再把需要备份的数据放到这个新对象,但这就暴露了对象内部的细节。
-
-
备忘录模式解决
-
UML
-
代码:
Caretaker
public class Caretaker { //如果只保存一次状态 private Memento memento; //对GameRole保存多次状态 private List<Memento> mementos; //如果对多个游戏角色保存多个状态 private HashMap<String, ArrayList<Memento>> hashMap; public Memento getMemento() { return memento; } public void setMemento(Memento memento) { this.memento = memento; } }
GameRole
public class GameRole { private int vit; private int def; //创建Memento,根据当前状态得到Memento public Memento createMemento() { return new Memento(vit, def); } //从备忘录对象恢复GameRole状态 public void recoverGameRole(Memento memento) { this.vit=memento.getVit(); this.def=memento.getDef(); } //显示当前游戏角色状态 public void show() { System.out.println("当前攻击力:"+this.vit+"当前防御力"+this.def); } public int getVit() { return vit; } public void setVit(int vit) { this.vit = vit; } public int getDef() { return def; } public void setDef(int def) { this.def = def; } }
Memento
public class Memento { private int vit; private int def; public Memento(int vit, int def) { super(); this.vit = vit; this.def = def; } public int getVit() { return vit; } public void setVit(int vit) { this.vit = vit; } public int getDef() { return def; } public void setDef(int def) { this.def = def; } }
Client
public class Client { public static void main(String[] args) { // TODO Auto-generated method stub //创建游戏角色 GameRole gameRole = new GameRole(); gameRole.setVit(100); gameRole.setDef(100); System.out.println("和boss大战前的状态"); gameRole.show(); //把当前状态保存caretaker Caretaker caretaker = new Caretaker(); caretaker.setMemento(gameRole.createMemento()); System.out.println("和boss大战~~~"); gameRole.setDef(30); gameRole.setVit(30); gameRole.show(); System.out.println("大战后,使用备忘录对象恢复到站前"); gameRole.recoverGameRole(caretaker.getMemento()); System.out.println("恢复后的状态"); gameRole.show(); } }
-
解释器模式
介绍与原理类图
-
介绍
- 在编译原理中,一个算术表达式通过词法分析器形成词法单元,而后这些词法单元再通过语法分析器构建语法分析树,最终形成一颗抽象的语法分析树。这里的词法分析器和语法分析器都可以看做是解释器。
- 解释器模式(Interpreter Pattern):是指给定一个语言(表达式),定义它的文法的一种表示,并定义一个解释器,使用该解释器来解释语言中的句子(表达式)。
- 应用场景:
- 应用可以将一个需要解释执行的语言中的句子表示为一个抽象语法树 • 一些重复出现的问题可以用一种简单的语言来表达 • 一个简单语法需要解释的场景
- 这样的例子还有,比如编译器、运算表达式计算、正则表达式、机器人等
-
原理类图
-
Context: 是环境角色,含有解释器之外的全局信息.
-
AbstractExpression: 抽象表达式, 声明一个抽象的解释操作,这个方法为抽象语法树中所有的节点所共享
-
TerminalExpression: 为终结符表达式, 实现与文法中的终结符相关的解释操作
-
NonTermialExpression: 为非终结符表达式,为文法中的非终结符实现解释操作.
说明: 输入Context 和 TerminalExpression 信息通过Client 输入即可
-
实例讲解
-
案例介绍
通过解释器模式来实现四则运算,如计算a+b-c的值,具体要求
- 先输入表达式的形式,比如 a+b+c-d+e, 要求表达式的字母不能重复
- 在分别输入a ,b, c, d, e 的值
- 最后求出结果:如图
-
传统方法解决
- 编写一个方法,接收表达式的形式,然后根据用户输入的数值进行解析,得到结果
- 如果加入新的运算符,比如 * / ( 等等,不利于扩展,另外让一个方法来解析会造成程序结构混乱,不够清晰.
-
解释器模式解决
-
UML
-
代码:
Expression
public abstract class Expression { // a + b - c // 解释公式和数值, key 就是公式(表达式) 参数[a,b,c], value就是就是具体值 // HashMap {a=10, b=20} 存的是表达式中各个变量的值 public abstract int interpreter(HashMap<String, Integer> var); }
VarExpression
/** * 变量的解释器 * @author Administrator * */ public class VarExpression extends Expression { private String key; // key=a,key=b,key=c public VarExpression(String key) { this.key = key; } // var 就是{a=10, b=20} // interpreter 根据 变量名称,返回对应值 @Override public int interpreter(HashMap<String, Integer> var) { return var.get(this.key); } }
SymbolExpression
/** * 抽象运算符号解析器 这里,每个运算符号,都只和自己左右两个数字有关系, * 但左右两个数字有可能也是一个解析的结果,无论何种类型,都是Expression类的实现类 * * @author Administrator * */ public class SymbolExpression extends Expression { protected Expression left; protected Expression right; public SymbolExpression(Expression left, Expression right) { this.left = left; this.right = right; } //因为 SymbolExpression 是让其子类来实现,因此 interpreter 是一个默认实现 @Override public int interpreter(HashMap<String, Integer> var) { // TODO Auto-generated method stub return 0; } }
SubExpression
public class SubExpression extends SymbolExpression { public SubExpression(Expression left, Expression right) { super(left, right); } //求出left 和 right 表达式相减后的结果 public int interpreter(HashMap<String, Integer> var) { return super.left.interpreter(var) - super.right.interpreter(var); } }
AddExpression
/** * 加法解释器 * @author Administrator * */ public class AddExpression extends SymbolExpression { public AddExpression(Expression left, Expression right) { super(left, right); } //处理相加 //var 仍然是 {a=10,b=20}.. //一会我们debug 源码,就ok public int interpreter(HashMap<String, Integer> var) { //super.left.interpreter(var) : 返回 left 表达式对应的值 a = 10 //super.right.interpreter(var): 返回right 表达式对应值 b = 20 return super.left.interpreter(var) + super.right.interpreter(var); } }
Calculator
public class Calculator { // 定义表达式 private Expression expression; // 构造函数传参,并解析 public Calculator(String expStr) { // expStr = a+b // 安排运算先后顺序 Stack<Expression> stack = new Stack<>(); // 表达式拆分成字符数组 char[] charArray = expStr.toCharArray();// [a, +, b] Expression left = null; Expression right = null; //遍历我们的字符数组, 即遍历 [a, +, b] //针对不同的情况,做处理 for (int i = 0; i < charArray.length; i++) { switch (charArray[i]) { case '+': // left = stack.pop();// 从stack取出left => "a" right = new VarExpression(String.valueOf(charArray[++i]));// 取出右表达式 "b" stack.push(new AddExpression(left, right));// 然后根据得到left 和 right 构建 AddExpresson加入stack break; case '-': // left = stack.pop(); right = new VarExpression(String.valueOf(charArray[++i])); stack.push(new SubExpression(left, right)); break; default: //如果是一个 Var 就创建要给 VarExpression 对象,并push到 stack stack.push(new VarExpression(String.valueOf(charArray[i]))); break; } } //当遍历完整个 charArray 数组后,stack 就得到最后Expression this.expression = stack.pop(); } public int run(HashMap<String, Integer> var) { //最后将表达式a+b和 var绑定 = {a=10,b=20} //然后传递给expression的interpreter进行解释执行 return this.expression.interpreter(var); } }
ClientTest
public class ClientTest { public static void main(String[] args) throws IOException { // TODO Auto-generated method stub String expStr = getExpStr(); // a+b HashMap<String, Integer> var = getValue(expStr);// var {a=10, b=20} Calculator calculator = new Calculator(expStr); System.out.println("运算结果:" + expStr + "=" + calculator.run(var)); } // 获得表达式 public static String getExpStr() throws IOException { System.out.print("请输入表达式:"); return (new BufferedReader(new InputStreamReader(System.in))).readLine(); } // 获得值映射 public static HashMap<String, Integer> getValue(String expStr) throws IOException { HashMap<String, Integer> map = new HashMap<>(); for (char ch : expStr.toCharArray()) { if (ch != '+' && ch != '-') { if (!map.containsKey(String.valueOf(ch))) { System.out.print("请输入" + String.valueOf(ch) + "的值:"); String in = (new BufferedReader(new InputStreamReader(System.in))).readLine(); map.put(String.valueOf(ch), Integer.valueOf(in)); } } } return map; } }
-
状态模式
介绍与原理类图
-
介绍
- 状态模式(State Pattern):它主要用来解决对象在多种状态转换时,需要对外输出不同的行为的问题。状态和行为是一一对应的,状态之间可以相互转换
- 当一个对象的内在状态改变时,允许改变其行为,这个对象看起来像是改变了其类
-
原理类图
- Context 类为环境角色, 用于维护State实例,这个实例定义当前状态
- State 是抽象状态角色,定义一个接口封装与Context 的一个特点接口相关行为
- ConcreteState 具体的状态角色,每个子类实现一个与Context 的一个状态相关行为
实例讲解
-
案例说明
-
UML
-
代码
state
public abstract class State { // 扣除积分 - 50 public abstract void deductMoney(); // 是否抽中奖品 public abstract boolean raffle(); // 发放奖品 public abstract void dispensePrize(); }
NoRaffleState
/** * 不能抽奖状态 * @author Administrator * */ public class NoRaffleState extends State { // 初始化时传入活动引用,扣除积分后改变其状态 RaffleActivity activity; public NoRaffleState(RaffleActivity activity) { this.activity = activity; } // 当前状态可以扣积分 , 扣除后,将状态设置成可以抽奖状态 @Override public void deductMoney() { System.out.println("扣除50积分成功,您可以抽奖了"); activity.setState(activity.getCanRaffleState()); } // 当前状态不能抽奖 @Override public boolean raffle() { System.out.println("扣了积分才能抽奖喔!"); return false; } // 当前状态不能发奖品 @Override public void dispensePrize() { System.out.println("不能发放奖品"); } }
CanRaffleState
/** * 可以抽奖的状态 * @author Administrator * */ public class CanRaffleState extends State { RaffleActivity activity; public CanRaffleState(RaffleActivity activity) { this.activity = activity; } //已经扣除了积分,不能再扣 @Override public void deductMoney() { System.out.println("已经扣取过了积分"); } //可以抽奖, 抽完奖后,根据实际情况,改成新的状态 @Override public boolean raffle() { System.out.println("正在抽奖,请稍等!"); Random r = new Random(); int num = r.nextInt(10); // 10%中奖机会 if(num == 0){ // 改变活动状态为发放奖品 context activity.setState(activity.getDispenseState()); return true; }else{ System.out.println("很遗憾没有抽中奖品!"); // 改变状态为不能抽奖 activity.setState(activity.getNoRafflleState()); return false; } } // 不能发放奖品 @Override public void dispensePrize() { System.out.println("没中奖,不能发放奖品"); } }
DispenseState
/** * 发放奖品的状态 * @author Administrator * */ public class DispenseState extends State { // 初始化时传入活动引用,发放奖品后改变其状态 RaffleActivity activity; public DispenseState(RaffleActivity activity) { this.activity = activity; } // @Override public void deductMoney() { System.out.println("不能扣除积分"); } @Override public boolean raffle() { System.out.println("不能抽奖"); return false; } //发放奖品 @Override public void dispensePrize() { if(activity.getCount() > 0){ System.out.println("恭喜中奖了"); // 改变状态为不能抽奖 activity.setState(activity.getNoRafflleState()); }else{ System.out.println("很遗憾,奖品发送完了"); // 改变状态为奖品发送完毕, 后面我们就不可以抽奖 activity.setState(activity.getDispensOutState()); //System.out.println("抽奖活动结束"); //System.exit(0); } } }
DispenseOutState
/** * 奖品发放完毕状态 * 说明,当我们activity 改变成 DispenseOutState, 抽奖活动结束 * @author Administrator * */ public class DispenseOutState extends State { // 初始化时传入活动引用 RaffleActivity activity; public DispenseOutState(RaffleActivity activity) { this.activity = activity; } @Override public void deductMoney() { System.out.println("奖品发送完了,请下次再参加"); } @Override public boolean raffle() { System.out.println("奖品发送完了,请下次再参加"); return false; } @Override public void dispensePrize() { System.out.println("奖品发送完了,请下次再参加"); } }
RaffleActivity
/** * 抽奖活动 // * * @author Administrator * */ public class RaffleActivity { // state 表示活动当前的状态,是变化 State state = null; // 奖品数量 int count = 0; // 四个属性,表示四种状态 State noRafflleState = new NoRaffleState(this); State canRaffleState = new CanRaffleState(this); State dispenseState = new DispenseState(this); State dispensOutState = new DispenseOutState(this); //构造器 //1. 初始化当前的状态为 noRafflleState(即不能抽奖的状态) //2. 初始化奖品的数量 public RaffleActivity( int count) { this.state = getNoRafflleState(); this.count = count; } //扣分, 调用当前状态的 deductMoney public void debuctMoney(){ state.deductMoney(); } //抽奖 public void raffle(){ // 如果当前的状态是抽奖成功 if(state.raffle()){ //领取奖品 state.dispensePrize(); } } public State getState() { return state; } public void setState(State state) { this.state = state; } //这里请大家注意,每领取一次奖品,count-- public int getCount() { int curCount = count; count--; return curCount; } public void setCount(int count) { this.count = count; } public State getNoRafflleState() { return noRafflleState; } public void setNoRafflleState(State noRafflleState) { this.noRafflleState = noRafflleState; } public State getCanRaffleState() { return canRaffleState; } public void setCanRaffleState(State canRaffleState) { this.canRaffleState = canRaffleState; } public State getDispenseState() { return dispenseState; } public void setDispenseState(State dispenseState) { this.dispenseState = dispenseState; } public State getDispensOutState() { return dispensOutState; } public void setDispensOutState(State dispensOutState) { this.dispensOutState = dispensOutState; } }
ClientTest
/** * 状态模式测试类 * @author Administrator * */ public class ClientTest { public static void main(String[] args) { // TODO Auto-generated method stub // 创建活动对象,奖品有1个奖品 RaffleActivity activity = new RaffleActivity(1); // 我们连续抽300次奖 for (int i = 0; i < 30; i++) { System.out.println("--------第" + (i + 1) + "次抽奖----------"); // 参加抽奖,第一步点击扣除积分 activity.debuctMoney(); // 第二步抽奖 activity.raffle(); } } }
策略模式
基本介绍与原理类图
-
介绍
-
策略模式(Strategy Pattern)中,定义算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户
-
这算法体现了几个设计原则:
第一、把变化的代码从不变的代码中分离出来;
第二、针对接口编程而不是具体类(定义了策略接口;
第三、多用组合/聚合,少用继承(客户通过组合方式使用策略)。
-
-
原理类图
从上图可以看到,客户context 有成员变量strategy或者其他的策略接口,至于需要使用到哪个策略,我们可以在构造器中指定
实例讲解
-
案例说明
- 有各种鸭子(比如 野鸭、北京鸭、水鸭等, 鸭子有各种行为,比如 叫、飞行等
- 显示鸭子的信息
-
传统方式解决
-
类图
-
代码:
Duck
public abstract class Duck { public Duck() { } public abstract void display(); public void quack() { System.out.println("鸭子嘎嘎叫"); } public void swim() { System.out.println("鸭子会游泳"); } public void fly() { System.out.println("鸭子会飞翔~~~"); } }
WildDuck
public class WildDuck extends Duck{ @Override public void display() { // TODO Auto-generated method stub System.out.println("这是野鸭"); } }
BeijingDuck
public class BeijingDuck extends Duck{ @Override public void display() { // TODO Auto-generated method stub System.out.println("这是北京鸭"); } //北京鸭不能飞翔 @Override public void fly() { // TODO Auto-generated method stub System.out.println("北京鸭不能飞翔"); } }
ToyDuck
public class ToyDuck extends Duck{ @Override public void display() { // TODO Auto-generated method stub System.out.println("这是玩具鸭"); } //需要重写父类所有方法,继承的意义何在 @Override public void quack() { System.out.println("玩具鸭不能叫"); } @Override public void swim() { System.out.println("玩具鸭不能游泳"); } @Override public void fly() { System.out.println("玩具鸭不能飞翔"); } }
-
问题分析
- 其它鸭子,都继承了Duck类,所以fly让所有子类都会飞了,这是不正确的
- 上面说的1 的问题,其实是继承带来的问题:对类的局部改动,尤其父类的局部改动,会影响其他部分。会有溢出效应
- 为了改进1问题,我们可以通过覆盖fly 方法来解决 => 重写解决
- 问题又来了,如果我们有一个玩具鸭子ToyDuck, 这样就需要ToyDuck去覆盖Duck的所有实现的方法
-
-
策略模式解决
-
UML
-
代码
Duck
public abstract class Duck { //属性,策略接口 FlyBehavior flybehavior; public Duck() { } public abstract void display(); public void quack() { System.out.println("鸭子嘎嘎叫"); } public void swim() { System.out.println("鸭子会游泳"); } public void fly() { if(flybehavior!=null) { flybehavior.fly(); } } }
WildDuck
public class WildDuck extends Duck{ public WildDuck() { // TODO Auto-generated constructor stub flybehavior=new GoodFlyBehavior(); } @Override public void display() { // TODO Auto-generated method stub System.out.println("这是野鸭"); } }
ToyDuck
public class ToyDuck extends Duck{ public ToyDuck() { // TODO Auto-generated constructor stub flybehavior=new NoFlyBehavior(); } @Override public void display() { // TODO Auto-generated method stub System.out.println("这是玩具鸭"); } //需要重写父类所有方法,继承的意义何在 @Override public void quack() { System.out.println("玩具鸭不能叫"); } @Override public void swim() { System.out.println("玩具鸭不能游泳"); } }
BeijingDuck
public class BeijingDuck extends Duck{ public BeijingDuck() { // TODO Auto-generated constructor stub flybehavior=new BadFlyBehavior(); } @Override public void display() { // TODO Auto-generated method stub System.out.println("这是北京鸭"); } }
FlyBehavior
public interface FlyBehavior { public void fly(); }
GoodFlyBehavior
public class GoodFlyBehavior implements FlyBehavior{ @Override public void fly() { // TODO Auto-generated method stub System.out.println("飞翔技术高超"); } }
BadFlyBehavior
public class BadFlyBehavior implements FlyBehavior{ @Override public void fly() { // TODO Auto-generated method stub System.out.println("飞翔技术一般"); } }
NoFlyBehavior
public class NoFlyBehavior implements FlyBehavior{ @Override public void fly() { // TODO Auto-generated method stub System.out.println("不会飞"); } }
Client
public class Client { public static void main(String[] args) { new WildDuck().fly(); new BeijingDuck().fly(); new ToyDuck().fly(); } }
-
职责链模式
基本介绍与原理类图
-
介绍
-
职责链模式(Chain of Responsibility Pattern), 又叫 责任链模式,为请求创建了一个接收者对象的链(简单示意图)。这种模式对请求的发送者和接收者进行解耦。
-
职责链模式通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
-
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
-
-
原理类图
- Handler : 抽象的处理者, 定义了一个处理请求的接口, 同时含义另外Handler
- ConcreteHandlerA , B :是具体的处理者, 处理它自己负责的请求, 可以访问它的后继者(即下一个处理者), 如果可以处理当前请求,则处理,否则就将该请求交个 后继者去处理,从而形成一个职责链
- Request , 含义很多属性,表示一个请求
实例讲解
-
案例说明
-
传统方式解决与问题分析
- 方式:使用到 分支判断(比如 switch) 来对不同的采购请求处理
- 问题:
- 如果各个级别的人员审批金额发生变化,在客户端的也需要变化
- 客户端必须明确的知道有多少个审批级别和访问
- 对一个采购请求进行处理 和 Approver (审批人) 就存在强耦合关系,不利于代码的扩展和维护。
-
职责链模式解决:
-
UML
-
代码
PurchaseRequest
public class PurchaseRequest { private int type; private float price=0.0f; private int id=0; public PurchaseRequest(int type, float price, int id) { super(); this.type = type; this.price = price; this.id = id; } public int getType() { return type; } public float getPrice() { return price; } public int getId() { return id; } }
Approver
public abstract class Approver { Approver approver;//下一个处理者 String name; public Approver(String name) { // TODO Auto-generated constructor stub this.name=name; } //下一个处理者 public void setApprover(Approver approver) { this.approver = approver; } //处理审批请求的方法,得到一个请求,处理是子类完成,该方法做成抽象 public abstract void processRequest(PurchaseRequest purchaseRequest); }
DepartmentApprover
public class DepartmentApprover extends Approver{ public DepartmentApprover(String name) { super(name); // TODO Auto-generated constructor stub } @Override public void processRequest(PurchaseRequest purchaseRequest) { // TODO Auto-generated method stub if(purchaseRequest.getPrice()<5000) { System.out.println("请求编号id="+purchaseRequest.getId()+"被" +this.name+"处理"); }else { approver.processRequest(purchaseRequest); } } }
CollegeApprover
public class CollegeApprover extends Approver{ public CollegeApprover(String name) { super(name); // TODO Auto-generated constructor stub } @Override public void processRequest(PurchaseRequest purchaseRequest) { // TODO Auto-generated method stub if(purchaseRequest.getPrice()<=10000) { System.out.println("请求编号id="+purchaseRequest.getId()+"被" +this.name+"处理"); }else { approver.processRequest(purchaseRequest); } } }
ViceSchoolMasterApprover
public class ViceSchoolMasterApprover extends Approver{ public ViceSchoolMasterApprover(String name) { // TODO Auto-generated constructor stub super(name); } @Override public void processRequest(PurchaseRequest purchaseRequest) { // TODO Auto-generated method stub if(purchaseRequest.getPrice() <= 30000) { System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理"); }else { approver.processRequest(purchaseRequest); } } }
SchoolMasterApprover
public class SchoolMasterApprover extends Approver{ public SchoolMasterApprover(String name) { // TODO Auto-generated constructor stub super(name); } @Override public void processRequest(PurchaseRequest purchaseRequest) { // TODO Auto-generated method stub if(purchaseRequest.getPrice() > 30000) { System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理"); }else { approver.processRequest(purchaseRequest); } } }
Client
public class Client { public static void main(String[] args) { PurchaseRequest purchaseRequest=new PurchaseRequest(1, 31000, 1); DepartmentApprover departmentApprover=new DepartmentApprover("系主任"); CollegeApprover collegeApprover=new CollegeApprover("院长"); ViceSchoolMasterApprover viceSchoolMasterApprover = new ViceSchoolMasterApprover("副校长"); SchoolMasterApprover schoolMasterApprover=new SchoolMasterApprover("校长"); //需要将各个审批级别的下一个设置好 (处理人构成环形: ) departmentApprover.setApprover(collegeApprover); collegeApprover.setApprover(viceSchoolMasterApprover); viceSchoolMasterApprover.setApprover(schoolMasterApprover); schoolMasterApprover.setApprover(departmentApprover); departmentApprover.processRequest(purchaseRequest); } }
-
原文链接:https://www.bilibili.com/video/BV1G4411c7N4?p=147&spm_id_from=333.1007.top_right_bar_window_history.content.click