设计模式
设计模式的目的:
1.代码重用性
2.可读性
3.可扩展性
4.可靠性
5.使程序呈现高内聚,低耦合的特性
1.七大原则
1.单一职责原则
基本介绍
对类来说,即一个类应该只负责一项职责,如类A负责两个不同的职责:职责1,职责2,当职责1需求变更而改变A时,可能造成职责2执行错误,所以需要将类A的粒度分解为A1,A2
错误示范
public class SingleResponsibility01 {
public static void main(String[] args) {
Vehicle vehicle = new Vehicle();
vehicle.run("汽车");
vehicle.run("摩托车");
vehicle.run("飞机");
}
}
//交通工具类
class Vehicle {
public void run(String vehicle) {
System.out.println(vehicle + "在公路上运行");
}
}
方案一
public class SingleResponsibility02 {
public static void main(String[] args) {
RoadVehicle roadVehicle = new RoadVehicle();
roadVehicle.run("摩托车");
roadVehicle.run("汽车");
AirVehicle airVehicle = new AirVehicle();
airVehicle.run("飞机");
}
}
class RoadVehicle {
public void run(String vehicle) {
System.out.println(vehicle + "在公路上运行");
}
}
class AirVehicle {
public void run(String vehicle) {
System.out.println(vehicle + "在天空运行");
}
}
class WaterVehicle {
public void run(String vehicle) {
System.out.println(vehicle + "在水中运行");
}
}
方式二
public class SingleResponsibility03 {
public static void main(String[] args) {
Vehicle2 vehicle2 = new Vehicle2();
vehicle2.run("汽车");
vehicle2.runWater("轮船");
vehicle2.runAir("飞机");
}
}
//交通工具类
class Vehicle2 {
public void run(String vehicle) {
System.out.println(vehicle + "在公路上运行");
}
public void runAir(String vehicle) {
System.out.println(vehicle + "在天空中运行");
}
public void runWater(String vehicle) {
System.out.println(vehicle + "在水上运行");
}
}
注意
1.降低类的复杂度,一个类只负责一项职责
2.提高类的可读性,可维护性
3.降低变更引起的风险
4.通常情况下,我们应当遵守单一职责原则,只有逻辑足够简单,才可以在代码级违反单一职责原则;只有类中的方法数量足够少,可以在方法级别保持单一职责原则
2.接口隔离原则
基本介绍
客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上
错误示范
interface Interface1 {
void operation1();
void operation2();
void operation3();
void operation4();
void operation5();
}
class D implements Interface1 {
@Override
public void operation1() {
System.out.println("D 实现 operation1");
}
@Override
public void operation2() {
System.out.println("D 实现 operation2");
}
@Override
public void operation3() {
System.out.println("D 实现 operation3");
}
@Override
public void operation4() {
System.out.println("D 实现 operation4");
}
@Override
public void operation5() {
System.out.println("D 实现 operation5");
}
}
class B implements Interface1 {
@Override
public void operation1() {
System.out.println("B 实现 operation1");
}
@Override
public void operation2() {
System.out.println("B 实现 operation2");
}
@Override
public void operation3() {
System.out.println("B 实现 operation3");
}
@Override
public void operation4() {
System.out.println("B 实现 operation4");
}
@Override
public void operation5() {
System.out.println("B 实现 operation5");
}
}
class C {
public void depend1(Interface1 i) {
i.operation1();
}
public void depend4(Interface1 i) {
i.operation4();
}
public void depend5(Interface1 i) {
i.operation5();
}
}
class A {//A类通过接口依赖B
public void depend1(Interface1 i) {
i.operation1();
}
public void depend2(Interface1 i) {
i.operation2();
}
public void depend3(Interface1 i) {
i.operation3();
}
}
改进
1.类A通过接口interface1依赖B,类C通过接口interface1依赖类D,如果接口interface1对于类A和类C来说不是最小接口,那么类B和类D去实现他们不需要的方法
2.将接口interface1拆分为独立的几个接口,类A和类C与他们需要的接口建立依赖关系,也就是采用接口隔离的原则
3.接口interface1中出现的方法,根据实际情况拆分为三个接口
4.代码实现
改进
public class Segregation02 {
public static void main(String[] args) {
A a = new A();
a.depend1(new B());
a.depend2(new B());
a.depend3(new B());
C c = new C();
c.depend1(new D());
c.depend4(new D());
c.depend5(new D());
}
}
interface Interface1 {
void operation1();
}
interface Interface2 {
void operation2();
void operation3();
}
interface Interface3 {
void operation4();
void operation5();
}
class B implements Interface1,Interface2 {
@Override
public void operation1() {
System.out.println("B 实现 operation1");
}
@Override
public void operation2() {
System.out.println("B 实现 operation2");
}
@Override
public void operation3() {
System.out.println("B 实现 operation3");
}
}
class D implements Interface1,Interface3{
@Override
public void operation1() {
System.out.println("D 实现 operation1");
}
@Override
public void operation4() {
System.out.println("D 实现 operation4");
}
@Override
public void operation5() {
System.out.println("D 实现 operation5");
}
}
class C {
public void depend1(Interface1 i) {
i.operation1();
}
public void depend4(Interface3 i) {
i.operation4();
}
public void depend5(Interface3 i) {
i.operation5();
}
}
class A {//A类通过接口依赖B
public void depend1(Interface1 i) {
i.operation1();
}
public void depend2(Interface2 i) {
i.operation2();
}
public void depend3(Interface2 i) {
i.operation3();
}
}
3.依赖倒转原则
基本概念
1.高层模块不应该依赖底层模块,二者都应该依赖其抽象
2.抽象不应该依赖细节,细节应该依赖抽象
3.依赖倒转(倒置)的中心思想是面向接口编程
4.依赖倒转原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架构比细节为基础的架构要稳定的多。在java中,抽象指的是接口或抽象类,细节就是具体的实现类
5.使用接口或抽象类的目的是制定好会犯,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成
错误代码
public class DependecyInversion01 {
public static void main(String[] args) {
new Person().receive(new Email());
}
}
class Email {
public String getInfo() {
return "电子邮件信息:赵登选";
}
}
class Person {
public void receive(Email email) {
System.out.println(email.getInfo());
}
}
改进后
1.接口模式
public class DependecyInversion02 {
public static void main(String[] args) {
new Person().receive(new Email());
new Person().receive(new Weixin());
}
}
class Email implements IReceiver{
@Override
public String getInfo() {
return "电子邮件信息:赵登选";
}
}
class Weixin implements IReceiver {
@Override
public String getInfo() {
return "微信的信息:赵静怡";
}
}
interface IReceiver {
String getInfo();
}
class Person {
public void receive ( IReceiver iReceiver) {
System.out.println(iReceiver.getInfo());
}
}
2.构造模式
public class DependecyInversion03 {
public static void main(String[] args) {
Apple apple = new Apple();
Function function = new Function(apple);
function.open();
}
}
interface Phone {
void open();
}
class Apple implements Phone {
@Override
public void open() {
System.out.println("苹果手机打开啦");
}
}
class Function {
Phone phone;
public Function(Phone phone) {
this.phone = phone;
}
public void open() {
phone.open();
}
}
3.set模式
public class DependecyInversion03 {
public static void main(String[] args) {
Apple apple = new Apple();
Function function = new Function();
function.setPhone(apple);
function.open();
}
}
interface Phone {
void open();
}
class Apple implements Phone {
@Override
public void open() {
System.out.println("苹果手机打开啦");
}
}
class Function {
private Phone phone;
public Function() {
}
public Function(Phone phone) {
this.phone = phone;
}
public Phone getPhone() {
return phone;
}
public void setPhone(Phone phone) {
this.phone = phone;
}
public void open() {
phone.open();
}
}
注意事项
1.低层模块尽量都要有抽象类或接口,或者两者都有,程序稳定性更好。
2.变量的声明类型尽量是抽象类或接口,这样我们的变量引用和实际对象间,就存在一个缓冲区,利于程序扩展和优化
3.继承时遵循里氏替换原则
4.里氏替换原则
基本介绍
1.继承包含这样一个含义:父类中凡是已经实现好的方法,实际上是在设定规范和契约,虽然它不强制要求所有的子类必须遵循这些契约,但是如果子类对这些已经实现的方法任意修改,就会对整个继承体系造成破坏
2.继承再给程序设计带来遍历的同时,也带来了弊端。不如使用继承会给程序带来入侵性,程序的可移植性,增加对象间的耦合性,如果一个类别其他的类所继承,则当这个类需要修改时,必须考虑到所有的子类,并且父类修改后,所有设计到子类的功能都有可能产生故障
3.里氏替换原则在1988年,由麻省理工学院的一位性里的女士提出
4.如果对每个类型T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有的对象o1都替换成o2时,程序P的行为没有发生变化,那么类型T2是类型T1的子类型。换句话说,所有引用基类的地方必须能透明地使用其子类的对象
5.在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类的方法
6,里氏替换原则告诉我们,继承实际上让两个类耦合度增强了,在适当的情况下,可以通过聚合,组合,依赖来解决问题。
错误示范
public class Liskov {
public static void main(String[] args) {
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();
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;
}
}
class B extends A {
@Override
public int func1(int num1, int num2) {
return num1 + num2;
}
public int func2(int a,int b) {
return func1(a,b) + 9;
}
}
解决问题
public class Liskov {
public static void main(String[] args) {
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();
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 Base {
}
class A extends Base{
public int func1(int num1,int num2) {
return num1 - num2;
}
}
class B extends Base {
private A a = new A();
public int func1(int a,int b) {
return a + b;
}
public int func2(int a,int b) {
return func1(a,b) + 9;
}
public int func3(int a, int b) {
return this.a.func1(a,b);
}
}
5.开闭原则
基本介绍
1.开闭原则是编程中最基础,最重要的设计原则
2.一个软件实体如类,模块,函数应该对扩展开放(对提供方),对修改关闭(对使用方)。用抽象构建框架,用实现扩展细节
3.当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码实现变化
4.编程中遵循其他原则,以及使用设计模式的目的就是遵循开闭原则
代码
1.方式一
public class Ocp {
public static void main(String[] args) {
GraphicEditor editor = new GraphicEditor();
editor.drawRectangle(new Rectangle());
editor.drawCircle(new Circle());
}
}
class GraphicEditor {
public void dramShape(Shape s) {
if (s.m_type == 1) {
drawRectangle(s);
} else if (s.m_type == 2) {
drawCircle(s);
}
}
public void drawRectangle(Shape r) {
System.out.println("矩形");
}
public void drawCircle(Shape r) {
System.out.println("圆形");
}
}
class Shape {
int m_type;
}
class Rectangle extends Shape {
public Rectangle() {
super.m_type = 1;
}
}
class Circle extends Shape {
public Circle() {
super.m_type = 2;
}
}
优缺点
1.优点是比较好理解,简单易操作
2.缺点是违反了设计模式的ocp原则,即对扩展开放,对修改不关闭。即当我们给类增加新功能的时候,尽量不修改代码,或者尽量少修改代码
3.比如我们这时要新增加一个图形种类,我们需要做如下修改,修改的地方较多
4.代码演示
改进
public class Ocp {
public static void main(String[] args) {
GraphicEditor graphicEditor = new GraphicEditor();
graphicEditor.drawShape(new Rectangle());
graphicEditor.drawShape(new Circle());
graphicEditor.drawShape(new Triangle());
}
}
class GraphicEditor {
public void drawShape(Shape s) {
s.draw();
}
}
abstract class Shape {
int m_type;
public abstract void draw();
}
class Rectangle extends Shape {
public Rectangle() {
super.m_type = 1;
}
@Override
public void draw() {
System.out.println("绘制矩形");
}
}
class Circle extends Shape {
public Circle() {
super.m_type = 2;
}
@Override
public void draw() {
System.out.println("绘制圆形");
}
}
class Triangle extends Shape {
public Triangle() {
super.m_type = 3;
}
@Override
public void draw() {
System.out.println("绘制三角形");
}
}
6.迪米特法则
基本介绍
1.一个对象应该对其他对象保持最少的了解
2.类与类关系越密切,耦合度越大
3.即一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外除了提供的public 方法,不对外泄露任何信息
4.迪米特法则还有个更简单的定义:只与直接的朋友通信
5.直接的朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系, 我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖,关联,组合,聚合 等。其中,我们称出现成员变量,方法参数,方法返回值中的类为直接的朋友,而 出现在局部变量中的类不是直接的朋友。也就是说,陌生的类最好不要以局部变量 的形式出现在类的内部。
错误示范
public class Demeter01 {
public static void main(String[] args) {
SchoolManager schoolManager = new SchoolManager();
schoolManager.printAllEmployee(new CollegeManager());
}
}
class Employee {
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
class CollegeEmployee {
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
class CollegeManager {
public List<CollegeEmployee> getAllEmployee() {
List<CollegeEmployee> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
CollegeEmployee employee = new CollegeEmployee();
employee.setId("学院id=" + i);
list.add(employee);
}
return list;
}
}
class SchoolManager {
public List<Employee> getAllEmployee() {
List<Employee> list = new ArrayList<>();
for (int i = 0; i < 5; i++) {
Employee employee = new Employee();
employee.setId("学校总部的id=" + i);
list.add(employee);
}
return list;
}
public void printAllEmployee(CollegeManager sub) {
for (CollegeEmployee employee : sub.getAllEmployee()) {
System.out.println(employee.getId());
}
for (Employee employee : this.getAllEmployee()) {
System.out.println(employee.getId());
}
}
}
改进
public class Demeter02 {
public static void main(String[] args) {
SchoolManager schoolManager = new SchoolManager();
schoolManager.printAllEmployee(new CollegeManager());
}
}
class Employee {
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
class CollegeEmployee {
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
class CollegeManager {
public List<CollegeEmployee> getAllEmployee() {
List<CollegeEmployee> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
CollegeEmployee employee = new CollegeEmployee();
employee.setId("学院id=" + i);
list.add(employee);
}
return list;
}
public void printEmployee() {
for (CollegeEmployee employee : this.getAllEmployee()) {
System.out.println(employee.getId());
}
}
}
class SchoolManager {
public List<Employee> getAllEmployee() {
List<Employee> list = new ArrayList<>();
for (int i = 0; i < 5; i++) {
Employee employee = new Employee();
employee.setId("学校总部的id=" + i);
list.add(employee);
}
return list;
}
public void printAllEmployee(CollegeManager sub) {
sub.printEmployee();
for (Employee employee : this.getAllEmployee()) {
System.out.println(employee.getId());
}
}
}
注意事项
1.迪米特法则的核心是降低类之间的耦合
2.但是注意:由于每个类都减少了不必要的依赖,因此迪米特尔法则只是要求降低类间(对象间)耦合关系,并不是要求完美没有依赖关系
7.合成复用原则
通常类的复用分为继承复用和合成复用两种,继承复用虽然有简单和易实现的优点,但它也存在以下缺点:
继承复用破坏了类的封装性。因为继承会将父类的实现细节暴露给子类,父类对子类是透明的,所以这种复用又称为“白箱”复用
子类与父类的耦合度高。父类的实现的任何改变都会导致子类的实现发生变化,这不利于类的扩展与维护
它限制了复用的灵活性。从父类继承而来的实现是静态的,在编译时已经定义,所以在运行时不可能发生变化
采用组合或聚合复用时,可以将已有对象纳入新对象中,使之成为新对象的一部分,新对象可以调用已有对象的功能,它有以下优点:
它维持了类的封装性。因为成分对象的内部细节是新对象看不见的,所以这种复用又称为“黑箱”复用
新旧类之间的耦合度低。这种复用所需的依赖较少,新对象存取成分对象的唯一方法是通过成分对象的接口
复用的灵活性高。这种复用可以在运行时动态进行,新对象可以动态地引用与成分对象类型相同的对象
2.基本介绍
1.创建型模式:单例模式,抽象工厂模式,原型模式,建造者模式,工厂模式
2.结构型模式:适配器模式,桥接模式,装饰模式,组合模式,外观模式,享元模式,代理模式
3.行为型模式:模板方法模式,命令模式,访问者模式,迭代器模式,观察者模式,中介着模式,备忘录模式,解释器模式,状态模式,策略模式,职责链模式
3.单例模式
基本介绍
所谓类的单例设计模式,就是采取一定的方法保证整个软件系统中,对某个只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法
1.饿汉式(静态常量)
public class SingletonTest01 {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton singleton = Singleton.getInstance();
System.out.println(instance == singleton);
}
}
class Singleton {
private Singleton() {}
private final static Singleton singleton = new Singleton();
public static Singleton getInstance() {
return singleton;
}
}
优缺点
1.优点:这种方法比较简单,就是类装载的时候就完成实例化。避免了线程同步问题。
2.缺点:再类装载的时候就完成实例化,没有达到lazy Loading的效果。如果从始致终从未使用过这个实例,则会造成内存的浪费
3.这种方法基于classloder机制避免了多线程的同步问题,不过,instance在类装载时就实例化,在单例模式中大多数都是调用getInstance方法,但是导致类装载的原因有多种,因此不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化instance就没有达到lazy loading的效果
4.结论:这种单例模式可用,可能造成内存浪费
2.饿汉式(静态代码块)
public class SingletonTest02 {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton singleton = Singleton.getInstance();
System.out.println(instance == singleton);
}
}
class Singleton {
private static Singleton singleton;
static {
singleton = new Singleton();
}
private Singleton() {
}
public static Singleton getInstance() {
return singleton;
}
}
优缺点
1.这种方式和上面的方式其实类似,只不过将类实例化的过程放在了静态代码块中,也是类装载的时候,就是执行静态代码快中的代码,初始化类的实例。优缺点和上面一样
2.结论:这种单例模式可用,但是可能造成内存浪费
3.懒汉式(线程不安全)
public class SingletonTest03 {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
System.out.println(instance == instance1);
}
}
class Singleton {
private Singleton() {
}
private static Singleton singleton;
public static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
优缺点
1.起到了lazy loading的效果,到时只能在单线程下使用
2.如果在多个线程下,一个线程进入了if(singleton == null) 判断语句块,还未来的及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。所有在多线程下不可使用这种方式
3.结论:在实际开发中,不要使用这种方式
4.懒汉式(线程安全,同步方法)
public class SingletonTest04 {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
System.out.println(instance == instance1);
}
}
class Singleton {
private Singleton() {
}
private static Singleton singleton;
public static synchronized Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
优缺点
1.解决了线程不安全问题
2.效率太低了,每个线程在想或得类实例时候,执行getInstance()方法都要进行同步。而其实这个方法只执行一次实例化代码就够了,后面想获得该类实例直接return就行了。方法进行同步效率太低
3.结论:在实际开发中,不推荐这种方式
5.懒汉式(线程安全,同步代码块)
public class SingletonTest05 {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
System.out.println(instance == instance1);
}
}
class Singleton {
private Singleton() {
}
private static Singleton singleton;
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
singleton = new Singleton();
}
}
return singleton;
}
}
优缺点
1.这种方式,本意是相对第四种方法实现改进,因为前面同步方法效率太低,改进同步产生实例化代码块
2.但是这种同步并不能起到线程同步的作用。跟第3种实现方式遇到的情形一致,假如一个线程进入了if(singleton == null)判断语句块,还未来得及往下执行,另一个线程通过了这个判断语句,这时会产生多个实例
3.结论:在实际开发中,不能使用这种方式
6.双重检查
public class SingletonTest06 {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
System.out.println(instance == instance1);
}
}
class Singleton {
private Singleton(){}
private static volatile Singleton singleton;
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
优缺点
1.double-check概念是多线程开发中常使用到的,如代码中所示,我们进行了两次if(singleton == null)检查,这样就可以保证线程安全了。
2.这样,实例化对象,也可以避免反复进行方法同步
3.线程安全;延迟加载;效率较高
4.结论:在实际开发中,推荐使用这种单例设计模式
7.静态内部类
public class SingletonTest07 {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
System.out.println(instance == instance1);
}
}
class Singleton {
private Singleton() {}
private static class SingletonInstance {
private static final Singleton SINGLETON = new Singleton();
}
public static Singleton getInstance() {
return SingletonInstance.SINGLETON;
}
}
优缺点
1.这种方式采用了类装载的机制来保证初始化实例只有一个线程。
2.静态内部类方式在Singleton类被装载并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonInstance,从而完成Singleton的实例化
3.类的静态属性只会在第一次加载类的时候初始化,所以在这里,jvm帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。
4.优点:避免了线程不安全,利用静态内部类特点实现延迟加载,效率高。
5.结论:推荐使用
8.枚举
public class SingletonTest08 {
public static void main(String[] args) {
Singleton singleton = Singleton.SINGLETON;
Singleton singleton1 = Singleton.SINGLETON;
System.out.println(singleton1 == singleton);
}
}
enum Singleton {
SINGLETON;
public void sayOk() {
System.out.println("ok~");
}
}
优缺点
1.这里借助jdk1.5中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能方式反序列化重新创建对象。
2.这种方式是Effective java 作者Josh Bloch 提倡的方式
3.结论:推荐使用
9.单例模式注意事项
1.单例模式保证了系统内存中该类只存在一个对象,节省了系统的资源,对于一些需要频繁销毁的对象,使用单例模式可以提高系统性能
2.当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new
3.单例使用场景:需要频繁即进行创建和销毁的对象,创建对象是消耗过多或耗费资源过多(即:重量级对象),但又经常用到的对象,工具类对象,频繁访问数据库或文件的对象(比如数据源,session工厂等)
4.工厂设计模式
1.简单工厂模式
传统
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 String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class GreekPizza extends Pizza{
@Override
public void prepare() {
System.out.println("给希腊披萨 准备原材料");
}
}
public class CheesePizza extends Pizza{
@Override
public void prepare() {
System.out.println("给制作奶酪披萨 准备原材料");
}
}
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 {
break;
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}while (true);
}
private String getType() {
Scanner scanner = new Scanner(System.in);
System.out.println("input pizza type:");
String str = scanner.nextLine();
return str;
}
}
public class PizzaStore {
public static void main(String[] args) {
new OrderPizza();
}
}
优缺点
1.比较好理解,简单易操作
2.缺点是违反了设计模式的ocp原则,即对扩展开放,对修改关闭。即当我们给类增加新功能的时候,尽量不修改代码,或者可能少的修改代码
改进思路
分析:修改代码可以接受,但是如果我们在其他的地方也有创建Pizza的代码,就意味着也需要修改,而创建Pizza的代码,往往有多处
思路:把创建Pizza对象封装到一个类中,这样就有新的Pizza种类,只需要修改该类就可,其他有创建到Pizza对象的代码就不需要修改了-> 简单工厂模式
基本介绍
1.简单工厂模式是属于创建型模式,是工厂模式的一种。简单工厂模式是由一个工厂决定创建出那一种产品类的实例。简单工厂模式是工厂模式家族中简单实用的模式
2.简单工厂模式:定义了一个创建对象的类,有这个类来封装实例化对象的行为(代码)
3.在软件开发中,当我们会用到大量的创建某种,某类或者某批对象时,就会使用到工厂模式
代码
public class SimpleFactory {
public Pizza createPizza(String orderType) {
System.out.println("使用简单工厂模式");
Pizza pizza = null;
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 class OrderPizza {
public OrderPizza(SimpleFactory simpleFactory) {
setSimpleFactory(simpleFactory);
}
private SimpleFactory simpleFactory;
private Pizza pizza = null;
public void setSimpleFactory(SimpleFactory simpleFactory) {
String orderType = "";
this.simpleFactory = simpleFactory;
do {
orderType = getType();
pizza = this.simpleFactory.createPizza(orderType);
if (!Objects.isNull(pizza)) {
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} else {
System.out.println("订购失败!");
break;
}
} while (true);
}
private String getType() {
Scanner scanner = new Scanner(System.in);
System.out.println("input pizza type:");
String str = scanner.nextLine();
return str;
}
}
public class PizzaStore {
public static void main(String[] args) {
new OrderPizza(new SimpleFactory());
}
}
2.工厂方法模式
工厂方法模式设计方案:将披萨项目的实例化功能抽象成抽象方法,在不同的口味餐子类中具体实现
工厂方法模式:定义了一个创建对象的抽象方法,由子类决定要实例化的类。工厂方法模式将对象的实例化推迟到子类
代码
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 String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class LDGreekPizza extends Pizza{
@Override
public void prepare() {
super.setName("伦敦希腊披萨");
System.out.println("给北京的伦敦披萨 准备原材料");
}
}
public class LDCheesePizza extends Pizza{
@Override
public void prepare() {
super.setName("伦敦奶酪披萨");
System.out.println("给伦敦的奶酪披萨 准备原材料");
}
}
public class BJGreekPizza extends Pizza{
@Override
public void prepare() {
super.setName("北京希腊披萨");
System.out.println("给北京的希腊披萨 准备原材料");
}
}
public class BJCheesePizza extends Pizza{
@Override
public void prepare() {
super.setName("北京奶酪披萨");
System.out.println("给北京的奶酪披萨 准备原材料");
}
}
public abstract class OrderPizza {
public OrderPizza() {
Pizza pizza = null;
String orderType = null;
do {
orderType = getType();
pizza = createPizza(orderType);
if (!Objects.isNull(pizza)) {
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} else {
System.out.println("订购失败");
break;
}
} while (true);
}
public abstract Pizza createPizza(String orderType);
private String getType() {
Scanner scanner = new Scanner(System.in);
System.out.println("input pizza type:");
String str = scanner.nextLine();
return str;
}
}
public class LDOrderPizza extends OrderPizza{
@Override
public Pizza createPizza(String orderType) {
Pizza pizza = null;
if (orderType.equals("cheese")) {
pizza = new LDCheesePizza();
} else if (orderType.equals("greek")) {
pizza = new LDGreekPizza();
}
return pizza;
}
}
public class BJOrderPizza extends OrderPizza{
@Override
public Pizza createPizza(String orderType) {
Pizza pizza = null;
if (orderType.equals("cheese")) {
pizza = new BJCheesePizza();
} else if (orderType.equals("greek")) {
pizza = new BJGreekPizza();
}
return pizza;
}
}
public class PizzaStore {
public static void main(String[] args) {
new BJOrderPizza();
}
}
3.抽象工厂模式
基本介绍
1.抽象工厂模式:定义一个interface用于创建相关或有依赖关系的对象族,二无需指明具体的类
2.抽象工厂模式可以将简单模式和工厂方法模式进行整合
3.将工厂抽象成两层,AbsFactory(抽象工厂) 和具体实现的工厂子类。程序员可以根据创建类型使用对应的工厂子类。这样将单个的简单的工厂类变成了工厂族更利于代码的维护和扩展
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 String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class LDGreekPizza extends Pizza {
@Override
public void prepare() {
super.setName("伦敦希腊披萨");
System.out.println("给北京的伦敦披萨 准备原材料");
}
}
public class LDCheesePizza extends Pizza {
@Override
public void prepare() {
super.setName("伦敦奶酪披萨");
System.out.println("给伦敦的奶酪披萨 准备原材料");
}
}
public class BJGreekPizza extends Pizza {
@Override
public void prepare() {
super.setName("北京希腊披萨");
System.out.println("给北京的希腊披萨 准备原材料");
}
}
public class BJCheesePizza extends Pizza {
@Override
public void prepare() {
super.setName("北京奶酪披萨");
System.out.println("给北京的奶酪披萨 准备原材料");
}
}
public interface AbsFactory {
public Pizza createPizza(String orderType);
}public class BJFactory implements AbsFactory{
@Override
public Pizza createPizza(String orderType) {
Pizza pizza = null;
if (orderType.equals("cheese")) {
pizza = new BJCheesePizza();
} else if (orderType.equals("greek")) {
pizza = new BJGreekPizza();
}
return pizza;
}
}
public class LDFactory implements AbsFactory{
@Override
public Pizza createPizza(String orderType) {
Pizza pizza = null;
if (orderType.equals("cheese")) {
pizza = new LDCheesePizza();
} else if (orderType.equals("greek")) {
pizza = new LDGreekPizza();
}
return pizza;
}
}
public class OrderPizza {
AbsFactory absFactory;
public OrderPizza(AbsFactory absFactory) {
setAbsFactory(absFactory);
}
public void setAbsFactory(AbsFactory absFactory) {
Pizza pizza = null;
String orderType = null;
this.absFactory = absFactory;
do {
orderType = getType();
pizza = absFactory.createPizza(orderType);
if (!Objects.isNull(pizza)) {
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.cut();
} else {
System.out.println("订购失败");
break;
}
} while (true);
}
private String getType() {
Scanner scanner = new Scanner(System.in);
System.out.println("input pizza type:");
String str = scanner.nextLine();
return str;
}
}
public class PizzaStore {
public static void main(String[] args) {
new OrderPizza(new BJFactory());
}
}
4.工厂模式小结
1.工厂模式的意义:将实例化的代码提取出来,放到一个类中统一管理维护,达到和主项目的依赖关系的解耦。从而提高项目的扩展和维护性
2.三种工厂模式
3.设计模式的依赖原则
创建对象实例时,不要直接new类,而是把这个new类的动作放在一个工厂的方法中,并返回。有的书说,变量不要直接持有具体类的引用
不要让类继承具体类,而是继承抽象类或者实现接口
不要覆盖基类中已经实现的方法
5.原型模式
传统
public class Sheep {
private String name;
private int age;
private String color;
public Sheep(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", age=" + age +
", color='" + color + '\'' +
'}';
}
}
public static void main(String[] args) {
Sheep sheep = new Sheep("tom", 1, "白色");
Sheep sheep1 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
Sheep sheep2 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
Sheep sheep3 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
Sheep sheep4 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
System.out.println(sheep);
System.out.println(sheep1);
System.out.println(sheep2);
System.out.println(sheep3);
System.out.println(sheep4);
}
优缺点
1.优点是比较好理解,简单易操作
2.在创建新的对象时,总是需要重新获取原始对象的属性,如果创建的对象复杂时,效率较低
3,总是需要重新初始化对象,而不是动态地获得对象运行时的状态,不够灵活
4.改进的s思路分析
思路:java中Object类是所有类的跟类,Object类提供了一个clone()方法,该方法可以将一个java对象复制一份,但是需要实现clone的java类必须要时间一个接口Cloneable该接口表示该类能够复制且具有复制的能力 =》原型模式
基本介绍
1.原型模式(Prototype模式)是指:用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象
2.原型模式是一种创建型设计模式,允许一个对象在创建另外一个可定制的对象无需知道如何创建的细节
3.工作原理:通过将一个原型模式对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建,即对象.clone()
4.形象的解释:孙大圣拔出猴毛,变出其他大圣
public class Sheep implements Cloneable{
private String name;
private int age;
private String color;
public Sheep(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", age=" + age +
", color='" + color + '\'' +
'}';
}
@Override
protected Object clone() {
try {
return (Sheep) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
System.out.println(e.getMessage());
}
return null;
}
}
public static void main(String[] args) {
Sheep sheep = new Sheep("tom", 1, "白色");
Sheep sheep1 = (Sheep) sheep.clone();
Sheep sheep2 = (Sheep) sheep.clone();
Sheep sheep3 = (Sheep) sheep.clone();
Sheep sheep4 = (Sheep) sheep.clone();
}
浅拷贝
1.对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性复制一份新的对象。
2.对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组,某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给信的对象。因为实际上两个对象的该成员变量都指向一个实例。在这种情况下,在一个对象总修改该成员变量会影响到另一个对象的该成员变量值
3.前面我们克隆羊就是浅拷贝
4.浅拷贝是使用默认的clone()方法来实现
深拷贝
1.复制对象的所有基本数据类型的成员变量值
2.为所有应用于数据类型的成员变量申请储存空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。也就是说,对象进行深拷贝要对整个对象进行拷贝
3.深拷贝实现方法一:重写clone方法来实现深拷贝
4.深拷贝实现方法二:通过对象序列化实现深拷贝
public class DeepCloneableTarget implements Serializable,Cloneable {
private static final Long serialVersionUID = 1L;
private String cloneName;
private String cloneClass;
public DeepCloneableTarget(String cloneName, String cloneClass) {
this.cloneName = cloneName;
this.cloneClass = cloneClass;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class DeepProtoType implements Serializable,Cloneable {
public String name;
public DeepCloneableTarget deepCloneableTarget;
@Override
protected Object clone() throws CloneNotSupportedException {
Object deep = null;
deep = super.clone();
DeepProtoType deepProtoType = (DeepProtoType) deep;
deepProtoType.deepCloneableTarget = (DeepCloneableTarget) deepCloneableTarget.clone();
return deepProtoType;
}
public Object deepClone() {
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
try {
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(this);
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
return (DeepProtoType) ois.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
System.out.println(e.getMessage());
return null;
} finally {
try {
if (bos != null) {
bos.close();
}
if (oos != null) {
oos.close();
}
if (bis != null) {
bis.close();
}
if (bos != null) {
bos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws CloneNotSupportedException {
DeepProtoType p1 = new DeepProtoType();
p1.name = "zdx";
p1.deepCloneableTarget = new DeepCloneableTarget("sdasdas","asdas");
//DeepProtoType p2 = (DeepProtoType) p1.clone();
//System.out.println(p1.name + p1.hashCode() + p1.deepCloneableTarget.hashCode());
//System.out.println(p2.name + p2.hashCode() + p2.deepCloneableTarget.hashCode());
DeepProtoType p2 = (DeepProtoType) p1.deepClone();
System.out.println(p1.name + p1.hashCode() + p1.deepCloneableTarget.hashCode());
System.out.println(p2.name + p2.hashCode() + p2.deepCloneableTarget.hashCode());
}
注意事项
1.创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率
2.不用重新初始化对象,而是动态地获得对象运行时的状态
3.如果原始对象发生变化(增加或者减少属性),其他克隆对象的也会发生相应的编号无需修改代码
4.在实现克隆的时候可能需要比较复杂的代码
5.缺点:需要为每个类配备一个克隆的方法,这个对全新的类来说不是很难,但是对已有的类进行改造时,需要修改其源代码,违背了ocp原则,这点请注意
6.建造者模式
传统
public abstract class AbstractHouse {
public abstract void buildBasic();
public abstract void buildWalls();
public abstract void roofed();
public void build(){
buildBasic();
buildWalls();
roofed();
}
}
public class CommonHouse extends AbstractHouse{
@Override
public void buildBasic() {
System.out.println("给普通房子打地基");
}
@Override
public void buildWalls() {
System.out.println("给普通房子砌墙");
}
@Override
public void roofed() {
System.out.println("给普通房子封顶");
}
}
public static void main(String[] args) {
CommonHouse commonHouse = new CommonHouse();
commonHouse.build();
}
问题分析
1.优点是比较好理解,简单易操作
2.设计的程序简单,过于简单,没有设计缓存层对象,程序的扩展和维护不好,也就是说,这种设计方案,把产品和创建产品的过程封装在一起,耦合性增强了
3.解决方案:将产品和产品建造过程解耦 => 建造者模式
基本介绍
1.建造者模式又叫生成器模式,是一种对象构建模式。它可以将复杂对象的建造过程抽象出来(抽象类别),使这个对象过程的不同实现方法可以构造不同的表现(属性)的对象
2.建造者模式是一步一步创建一个不咋的对象,它允许用户只通过指定复杂对象的类型就可以构建它们,用户不需要直到内部的具体构建细节
建造者模式的四个角色
1.Product(产品角色):一个具体的产品对象
2.Builder(抽象建造者):创建一个Product对象的各个部件指定的接口/抽象类
3.ConncreteBuilder(具体建造者):实现接口,构建和装配各个部件
4.Director(指挥者):构建一个使用Builder接口的对象。它主要是用于创建一个复杂的对象。它主要有两个作用,一是:隔离了客户与对象的生产过程,二是:负责控制产品对象的生产过程
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;
}
}
public abstract class HouseBuilder {
protected final House house = new House();
public abstract void buildBasic();
public abstract void buildWall();
public abstract void roofed();
public House buildHouse(){
return house;
}
}
public class HighBuilding extends HouseBuilder{
@Override
public void buildBasic() {
System.out.println("给高楼打地基20米");
}
@Override
public void buildWall() {
System.out.println("给高楼砌墙400米");
}
@Override
public void roofed() {
System.out.println("给高楼封顶");
}
}
public class CommonHouse extends HouseBuilder{
@Override
public void buildBasic() {
System.out.println("普通房子打地基5米");
}
@Override
public void buildWall() {
System.out.println("普通房子砌墙10米");
}
@Override
public void roofed() {
System.out.println("普通房子封顶");
}
}
public class HouseDirector {
HouseBuilder houseBuilder = null;
public void setHouseBuilder(HouseBuilder houseBuilder) {
this.houseBuilder = houseBuilder;
}
public HouseDirector(HouseBuilder houseBuilder) {
this.houseBuilder = houseBuilder;
}
public House constructHouse() {
houseBuilder.buildBasic();
houseBuilder.buildWall();
houseBuilder.roofed();
return houseBuilder.buildHouse();
}
}
public static void main(String[] args) {
CommonHouse commonHouse = new CommonHouse();
HouseDirector houseDirector = new HouseDirector(commonHouse);
House house = houseDirector.constructHouse();
HighBuilding highBuilding = new HighBuilding();
houseDirector.setHouseBuilder(highBuilding);
houseDirector.constructHouse();
}
注意事项
1.客户端(使用程序)不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象
2.每一个具体的建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者,用户使用不同的具体建造者即可得到不同的产品对象
3.可以更加精细地控制产品的创建过程。将复杂的产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程
4.增加新的具体建造者无需修改原有类库的代码,指挥者类针对抽象建造者编程,系统扩展方便,符合“开闭原则”
5.建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受的一定的限制
6.如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统边的庞大,因此在这种情况下,要考虑是否选择建造者模式
7.抽象工厂模式vs建造者模式
抽象工厂模式实现产品家族的创建,一个产品家族是这样的一系列产品:具有不同分类维度的产品组合,采用抽象工厂模式不需要关心构建过程,只需要关心什么产品由什么工厂生产即可。而创建者模式则是要求按照指定的蓝图建造产品,它的主要目的是通过组装零配件而产生一个新的产品
7.适配器模式
基本介绍
1.适配器模式(Adapter Pattern)将某个类的接口转换成客户端期望的另一个接口表示,主要目的兼容性,让原来的接口不匹配不能一起工作的两个类可以协同工作。其别名为包装器(Wrapper)
2.适配器模式属于结构型模式
3.主要分为三类:类适配器模式,对象适配器模式,接口适配器模式
工作原理
1。适配器模式:将一个类的接口转换成另一种接口,让原本接口不兼容的类可以兼容
2.从用户的角度看不到被适配者,是解耦的
3.用户调用适配器转化出来的目标接口方法,适配器再调用被适配者的相关接口方法
4.用户收到反馈接口,感觉只是和目标接口交互
1.类适配器
public class Voltage220V {
public int output220() {
System.out.println("电压220v电压");
return 220;
}
}
public interface IVoltage5V {
public int output5V();
}
public class VoltageAdapter extends Voltage220V implements IVoltage5V{
@Override
public int output5V() {
int src = output220();
return src / 44;
}
}
public class Phone {
public void charging(IVoltage5V iVoltage5V) {
if (iVoltage5V.output5V() == 5) {
System.out.println("电压正常可以充电啦");
} else {
System.out.println("电压过高");
}
}
}
public static void main(String[] args) {
new Phone().charging(new VoltageAdapter());
}
类适配器的注意事项
1.java是单继承机制,所以类适配器需要继承src类这一点算是一个缺点,因为要求dst必须是接口,有一定局限性
2.src类的方法在Adapter中都会暴露出来,也增加了使用成本
3.由于继承src类,所以它可以根据需求重写src类的方法,使得Adapter活性曾强了
2.对象适配器
基本介绍
1.基本思路和类的适配器模式相同,只是将Adapter类作修改,不是继承src类,而是持有src类的实例,已解决兼容性的问题。即:持有src类,实现dst类接口,完成src-dst的适配
2.根据“合成复用原则”,在系统中尽量使用冠梁关系来替代继承关系
3.对象适配器模式是适配器模式常用的一种
public class Voltage220V {
public int output220V() {
System.out.println("电压220v");
return 220;
}
}
public interface Voltage5V {
public int output5V();
}
public class VoltageAdapter implements Voltage5V{
private Voltage220V v;
public VoltageAdapter(Voltage220V v) {
this.v = v;
}
@Override
public int output5V() {
int src = v.output220V();
return src / 44;
}
}
public class Phone {
public void charging(VoltageAdapter iVoltage5V) {
if (iVoltage5V.output5V() == 5) {
System.out.println("电压正常可以充电啦");
} else {
System.out.println("电压过高");
}
}
}
public static void main(String[] args) {
new Phone().charging(new VoltageAdapter(new Voltage220V()));
}
对象适配器注意事项
1.对象适配器和类适配器其实算是同一种思想,只不过实现方式不同。根据合成复用原则,使用组合替代继承,所以它解决了类适配器必须继承src的局限性问题,也不再要求dst必须是接口。
2.使用成本更低,更灵活
3.接口适配器模式
基本介绍
1.一些书籍称为:适配器模式(Default Adapter Pattern) 或缺省适配器模式
2.当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该类接口中每个方法提供一个默认的实现(空实现),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求
3.适用于一个接口不想使用其所有的方法的情况
public interface Interface4 {
public void m1();
public void m2();
public void m3();
public void m4();
public void m5();
}
public abstract class AbsAdapter implements Interface4{
@Override
public void m1() {
}
@Override
public void m2() {
}
@Override
public void m3() {
}
@Override
public void m4() {
}
@Override
public void m5() {
}
}
public static void main(String[] args) {
new AbsAdapter(){
@Override
public void m1() {
System.out.println("m1");
}
};
}
注意事项
1.三种命令方式,是根据src是以怎样的形式给到Adapter(在Adaper里的形式) 来命名的。
2.类适配器:以类给到,在Adapter里,就是将src当做类,继承
对象适配器:以对象给到,在Adapter里,将src作为一个接口,持有
接口适配器:以接口给到,在Adapter里,将src作为一个接口,实现
3.Adapter模式最大的作用还是将原本不兼容的接口融合在一起工作
4.实际开发中,实现起来不拘泥于我们讲解的三种经典形式
8.桥接模式
基本介绍
1.桥接模式(Bridge模式)是指:将实现于抽象放在两个不同的类层次中,使两个层次可以独立改变
2.是一种结构型设计模式
3.Bridge模式基本类的最小实际原则,通过使用封装,聚合及继承等行为让不同的类承担不同的职责。它的主要特点是把抽象(Abstraction)与行为实现(Implementation)分离开来,从而可以保持各部分的独立性以及应对他们的功能扩展
public interface Brand {
void open();
void close();
void call();
}
public class Vivo implements Brand{
@Override
public void open() {
System.out.println("Vivo开机");
}
@Override
public void close() {
System.out.println("Vivo关机");
}
@Override
public void call() {
System.out.println("Vivo打电话");
}
}
public class XiaoMi implements Brand{
@Override
public void open() {
System.out.println("小米手机开机");
}
@Override
public void close() {
System.out.println("小米手机关机");
}
@Override
public void call() {
System.out.println("小米手机打电话");
}
}
public abstract class Phone {
private Brand brand;
public Phone(Brand brand) {
this.brand = brand;
}
protected void open() {
this.brand.open();
}
protected void close() {
this.brand.close();
}
protected void call() {
this.brand.call();
}
}
public class FoldedPhone extends Phone{
public FoldedPhone(Brand brand) {
super(brand);
}
@Override
public void open() {
super.open();
System.out.println("折叠样式手机");
}
@Override
public void close() {
super.close();
System.out.println("折叠样式手机");
}
@Override
public void call() {
super.call();
System.out.println("折叠样式手机");
}
}
public class UpRightPhone extends Phone{
public UpRightPhone(Brand brand) {
super(brand);
}
@Override
public void open() {
super.open();
}
@Override
public void close() {
super.close();
}
@Override
public void call() {
super.call();
}
}
public static void main(String[] args) {
FoldedPhone phone = new FoldedPhone(new XiaoMi());
phone.open();
phone.close();
phone.call();
Phone phone1 = new UpRightPhone(new Vivo());
phone1.open();
phone1.close();
phone1.call();
}
注意事项
1.实现了抽象和实现的部分的分离,从而极大的提供了系统的灵活性,让抽象部分和实现部分独立开来,这有助于系统进行分层设计,从而产生更好的结构化系统。
2.对于系统的高层部分,只需要知道抽象部分和实现部分的接口就可以了,其它的部分由具体业务来完成
3.桥接模式替代多层继承方案,可以减少子类的个数,降低系统的管理和维护成本。
4,桥接模式的引用增加了系统的理解和设计难度,由于聚合关联的关系建立在抽象层,要求开发者针对抽象进行设计和编程
5.桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围有一定的局限性,既需要有这样的应用场景
应用场景
1,对于那些不希望使用继承或因为多层继承导致系统类的个数急剧增加的系统,倩姐模式无为适用
2.常见的应用场景
1.JDBC驱动程序
2.银行转账系统
2.1.转账分类:网上转账,柜台转账,AMT转 账
2.2转账用户类型:普通用户,银卡用户,金 卡用户
3.消息管理
消息类型:即时消息,延时消息
消息分类:手机短信,邮件消息,qq消息
9.装饰者设计模式
定义
1.装饰者模式:动态的将新功能附加到对象上。在对象功能扩展方面,它比继承有弹性,装饰者模式也体现了开闭原则
2.这里提到的动态将新功能附加到对象上和ocp原则,在后面的应用实例上会以代码的形式体现。
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();
}
public class Coffer extends Drink{
@Override
public float cost() {
return super.getPrice();
}
}
public class ShortBlack extends Coffer{
public ShortBlack() {
setDes("ShortBlack");
setPrice(4.0f);
}
}
public class LongBlack extends Coffer{
public LongBlack() {
setDes("美式咖啡");
setPrice(5.0f);
}
}
public class DeCaf extends Coffer{
public DeCaf() {
setDes("无印咖啡");
setPrice(3.0f);
}
}
public class Chocolate extends Decorator {
public Chocolate(Drink obj) {
super(obj);
setDes("巧克力");
setPrice(3.0f);
}
}
public class Espresso extends Coffer {
public Espresso() {
setDes("意大利咖啡");
setPrice(6.0f);
}
}
public class Decorator extends Drink{
private Drink obj;
public Decorator(Drink obj) {
this.obj = obj;
}
@Override
public float cost() {
return super.getPrice() + obj.cost();
}
@Override
public String getDes() {
return super.getDes() + " " + super.getPrice() + "&&" + obj.getDes();
}
}
public class Milk extends Decorator{
public Milk(Drink obj) {
super(obj);
setDes("牛奶");
setPrice(2.0f);
}
}
public class Soy extends Decorator {
public Soy(Drink obj) {
super(obj);
setDes("豆浆");
setPrice(1.5f);
}
}
public static void main(String[] args) {
Drink drink = new LongBlack();
System.out.println("费用一" + drink.cost());
System.out.println("描述:" + drink.getDes());
drink = new Milk(drink);
System.out.println("费用二" + drink.cost());
System.out.println("描述:" + drink.getDes());
drink = new Chocolate(drink);
System.out.println("费用三" + drink.cost());
System.out.println("描述:" + drink.getDes());
drink = new Milk(drink);
System.out.println("费用四" + drink.cost());
System.out.println("描述:" + drink.getDes());
System.out.println("订单二");
Drink order = new DeCaf();
System.out.println("费用一:" + order.cost());
System.out.println("描述:" + order.getDes());
order = new Milk(order);
System.out.println("费用二:" + order.cost());
System.out.println("描述:" + order.getDes());
}
io源码说明
1.InputStream 是抽象类,类似我们前面讲的Drink
2.FileInputStream 是InputStream 子类,类似我们前面的Decaf,LongBlack
3.FilterInputStream 是 inputStream 子类:类似我们前面的 Decorator 修饰着
4.DataInputStream 是FilterInputStream 子类,具体的修饰着,类似前面的 Milk,Soy 等
4.FileterInputStream 类有prorected volatile inputStream in;即含被修饰者
5.分析得出 jdk 的 io 体系中,就是使用装饰者模式
10.组合模式
基本介绍
1.组合模式(Commposite Pattern) ,又叫部分整体模式,他创建了对象组的树性结构,将对象组合成树状结构仪表示“整体-部分”的层次关系
2.组合模式依据树形结构来组合对对象,用来表示部分以及整体层次
3.这种类型的设计模式数据结构型设计模式。
4.组合模式使得用户对方单个对象和组合对象的访问具有一致性,即:组合能让客户一致的方式处理个别对象以及组合对象
解决的问题
1.组合模式解决这样的问题,当我们要处理的对象可以生成一颗树型结构,而我们要对树上的节点和叶子进行操作时,他能够提供一致的方式而不用考虑它节点还是叶子
public abstract class OrganizationComponent {
private String name;
private String des;
protected void add(OrganizationComponent component) {
throw new UnsupportedOperationException();
}
protected void remove(OrganizationComponent component) {
throw new UnsupportedOperationException();
}
public OrganizationComponent(String name, String des) {
this.name = name;
this.des = des;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDes() {
return des;
}
public void setDes(String des) {
this.des = des;
}
protected abstract void print();
}
public class University extends OrganizationComponent{
List<OrganizationComponent> organizationComponents = new ArrayList<>();
public University(String name, String des) {
super(name, des);
}
@Override
public void add(OrganizationComponent component) {
organizationComponents.add(component);
}
@Override
public void remove(OrganizationComponent component) {
organizationComponents.remove(component);
}
@Override
public String getName() {
return super.getName();
}
@Override
public String getDes() {
return super.getDes();
}
@Override
public void print() {
System.out.println("学校的名字" + getName());
for (OrganizationComponent organizationComponent : organizationComponents) {
organizationComponent.print();
}
}
}
public class College extends OrganizationComponent {
List<OrganizationComponent> organizationComponents = new ArrayList<>();
public College(String name, String des) {
super(name, des);
}
@Override
public void add(OrganizationComponent component) {
organizationComponents.add(component);
}
@Override
public void remove(OrganizationComponent component) {
organizationComponents.remove(component);
}
@Override
public String getDes() {
return super.getDes();
}
@Override
public String getName() {
return super.getName();
}
@Override
protected void print() {
System.out.println("学院的名字" + getName());
for (OrganizationComponent organizationComponent : organizationComponents) {
organizationComponent.print();
}
}
}
public class Department extends OrganizationComponent{
public Department(String name, String des) {
super(name, des);
}
@Override
public String getName() {
return super.getName();
}
@Override
public String getDes() {
return super.getDes();
}
@Override
protected void print() {
System.out.println(getName());
}
}
public static void main(String[] args) {
OrganizationComponent university = new University("清华大学", "中国顶级的大学");
OrganizationComponent college1 = new College("计算机学院", "计算机学院");
OrganizationComponent college2 = new College("信息工程学院", "信息工程学院");
college1.add(new Department("软件工程","软件工程"));
college1.add(new Department("信息安全","信息安全"));
college1.add(new Department("计算机科学与技术","计算机科学与技术"));
college2.add(new Department("信息工程","信息工程不好学"));
college2.add(new Department("通信工程","通信工程不好学"));
university.add(college1);
university.add(college2);
college1.print();
}
注意事项
1.简化客户点操作。客户端只需要面对一致的对象而不用考虑整体部分或者节点叶子的问题
2.具有较强的扩展性。当我们要更改组合对象,我们只需要调整内部的层次关系,客户端不用做出任何改动
3.方便创建出复杂的乘次结构。客户端不用理会组合里面的组成细节,容易添加节点或者叶子从而创建出复杂的树型结构
4.需要遍历组织机构,或者处理的对象具有树型结构时,非常适合使用组合模式
5.要求较高的抽象性,如果节点和叶子有很多的差异性的话,比较很多方法和属性都不一样,不适合使用组合模式
11.外观模式
基本介绍
1.外观模式(Facade) ,也叫“过程模式”:外观模式为子系统中的接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用
2.外观模式通过定义一个一致的接口,用已屏蔽内部子系统的细节,使得调用端只需跟这个接口发生调用,而无需关心这个子系统的内部细节
public class PopCorn {
private static final PopCorn POP_CORN = new PopCorn();
private PopCorn() {
}
public static PopCorn getInstance() {
return POP_CORN;
}
public void on() {
System.out.println("POP ON");
}
public void off() {
System.out.println("POP OFF");
}
public void pop() {
System.out.println("pop is pop");
}
}
public class Projector {
private static final Projector PROJECTOR = new Projector();
private Projector() {
}
public static Projector getInstance() {
return PROJECTOR;
}
public void on() {
System.out.println("PROJECTOR ON");
}
public void off() {
System.out.println("PROJECTOR OFF");
}
public void focus() {
System.out.println("PROJECTOR IS FOCUS");
}
}
public class Screen {
private static final Screen SCREEN = new Screen();
private Screen() {
}
public static Screen getInstance() {
return SCREEN;
}
public void up() {
System.out.println("SCREEN UP");
}
public void down() {
System.out.println("SCREEN DOWN");
}
}
public class Stereo {
private static final Stereo STEREO = new Stereo();
private Stereo() {
}
public static Stereo getInstance() {
return STEREO;
}
public void on() {
System.out.println("STEREO ON");
}
public void off() {
System.out.println("STEREO OFF");
}
public void up() {
System.out.println("STEREO UP");
}
}
public class TheaterLight {
private static final TheaterLight THEATER_LIGHT = new TheaterLight();
private TheaterLight() {
}
public static TheaterLight getInstance() {
return THEATER_LIGHT;
}
public void on() {
System.out.println("THEATER_LIGHT ON");
}
public void off() {
System.out.println("THEATER_LIGHT OFF");
}
public void dim() {
System.out.println("THEATER_LIGHT DIM");
}
public void bright() {
System.out.println("THEATER_LIGHT BRIGHT");
}
}
public class DVDPlayer {
private static final DVDPlayer DVD_PLAYER = new DVDPlayer();
private DVDPlayer() {
}
public static DVDPlayer getInstance() {
return DVD_PLAYER;
}
public void on () {
System.out.println("DVD ON");
}
public void off() {
System.out.println("DVD OFF");
}
public void play() {
System.out.println("DVD IS PLAY");
}
public void pause() {
System.out.println("DVD IS PAUSE");
}
}
public class HomeTheaterFacade {
private final DVDPlayer dvdPlayer = DVDPlayer.getInstance();
private final PopCorn popCorn = PopCorn.getInstance();
private final Projector projector = Projector.getInstance();
private final Screen screen = Screen.getInstance();
private final Stereo stereo = Stereo.getInstance();
private final TheaterLight theaterLight = TheaterLight.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.off();
screen.up();
projector.off();
stereo.off();
dvdPlayer.off();
}
public static void main(String[] args) {
}
}
public static void main(String[] args) {
HomeTheaterFacade facade = new HomeTheaterFacade();
facade.ready();
facade.play();
}
注意事项
1.外观模式对外屏蔽了子系统的细节,因此外观模式降低了客户端对子系统使用的复杂性
2.外观模式对客户端与子系统的耦合关系,让子系统内部的模块更易维护和扩展
3.通过合理的使用外观模式,可以帮我们更好的划分访问的层次
4.当系统需要进行分层设计时,可以考虑使用Fcade模式
5.在维护一个遗留的大型系统时,可能这个系统已经变得非常难以维护和扩展,此时可以考虑为新系统开发一个Facade类,来提供遗留系统的比较清晰简单的接口,让新系统与Facade类交互,提高复用性
6.不能过多的或者不合理的抵用外观模式,使用外观模式好,还是直接调用模块好。要以让系统有层次,利于维护目的。
12.享元模式
内部状态和外部状态
1.享元模式提出了两个要求:细粒度和共享对象。这里就涉及到内部状态和外部状态了,即将对象的信息分为两部分:内部状态和外部状态
2.内部状态指对象共享出来的信息,存储在享元对象内部且不会随环境的改变而改变
3.外部状态指对象得以依赖的一个标记,是随机环境而改变,不可共享的状态
public class User {
private String name;
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public abstract class WebSite {
public abstract void use(User user);
}
public class ConcreteWebSite extends WebSite{
private String type = " ";
public ConcreteWebSite(String type) {
this.type = type;
}
@Override
public void use(User user) {
System.out.println("网线的发布形式为:" + type + "在使用中~~~" + user.getName());
}
}
public class WebSiteFactory {
private Map<String,ConcreteWebSite> pool = new HashMap<>();
public WebSite getWebSite(String type) {
if (!pool.containsKey(type)) {
pool.put(type,new ConcreteWebSite(type));
}
return pool.get(type);
}
public int getCount() {
return pool.size();
}
}
public static void main(String[] args) {
WebSiteFactory factory = new WebSiteFactory();
WebSite webSite1 = factory.getWebSite("新闻");
webSite1.use(new User("张三"));
WebSite webSite2 = factory.getWebSite("博客");
webSite2.use(new User("李四"));
WebSite webSite3 = factory.getWebSite("博客");
webSite3.use(new User("王五"));
System.out.println(factory.getCount());
}
注意事项
1.在享元模式这样理解,“享”就表示共享,“元”表示对象
2.系统中有大量对象,这些对象消耗大量的内存,并且对象的状态大部分可以外部化时,我们就可以考虑选用享元模式
3.用唯一标识码判断,如果在内存中有,则返回这个唯一标识码所标识的对象,用HashMap/HashTable存储
4.享元模式大大减少了对象的创建,降低了程序内存的占用,提高效率
5.享元模式提高了系统的复杂度。需要分离出内部状态和外部状态,而外部状态具有固化特性,不应随着内部状态的改变而改变,这是我们是用享元模式需要注意的地方
6.是用享元模式时,注意划分内部状态和外部状态,并且需要有一个工厂类加一控制。
7.享元模式经典的应用场景是需要缓冲池的场景,不如String常量池,数据库连接池
13.代理模式
基本介绍
1.代理模式:为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象,这样做的好处是:可以在目标对象实现的基础上,曾强额外的功能操作,即扩展目标对象的功能
2.被代理的对象可以是远程对象,创建开销大的对象或许要安全控制的对象
3.代理模式有不同的形式,主要有三种 静态代理,动态代理和Cglib代理
1.静态代理
静态代理在使用时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者继承相同的父类
public interface ITeacherDao {
public void teach();
}
public class TeacherDao implements ITeacherDao{
@Override
public void teach() {
System.out.println("老师授课中");
}
}
public class TeacherDaoProxy implements ITeacherDao{
private ITeacherDao target;
public TeacherDaoProxy(ITeacherDao target) {
this.target = target;
}
@Override
public void teach() {
System.out.println("代理提交");
target.teach();
System.out.println("代理结束");
}
}
public static void main(String[] args) {
TeacherDao teacherDao = new TeacherDao();
TeacherDaoProxy proxy = new TeacherDaoProxy(teacherDao);
proxy.teach();
}
优缺点
1.优点:在不修改目标对象的功能的前提下,能通过代理对象对目标功能扩展
2.缺点:因为代理对象需要与目标对象实现一样的接口,所有会有跟多代理类
3.一旦接口增加方法,目标对象与代理对象都需要维护
2.动态代理
基本介绍
1.代理对象,不需要实现接口,但是目标对象要实现接口,否则不能动态代理
2.代理对象的生成,是利用JDK的apl,动态的在内存中构建对象
3.动态代理也叫作:JDK代理,接口代理
public interface ITeacherDao {
void teach();
}
public class TeacherDao implements ITeacherDao{
@Override
public void teach() {
System.out.println("老师在授课");
}
}
public class ProxyFactory {
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
public Object getPoxyInstance() {
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("JDK代理开始");
return method.invoke(target, args);
}
});
}
}
public static void main(String[] args) {
ITeacherDao teacherDao = new TeacherDao();
ProxyFactory proxyFactory = new ProxyFactory(teacherDao);
ITeacherDao poxyInstance = (ITeacherDao) proxyFactory.getPoxyInstance();
poxyInstance.teach();
}
3.Cglib代理
基本介绍
1.静态代理和JDK 代理模式都要求目标对象是实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候可使用目标独享子类来实现代理,这就是Cglib代理
2.Cglib代理也叫作子类代理,他在内存中构建一个子类对象从而实现对目标对象功能扩展,有些书也将Cglib代理归属到动态代理
3.Cglib是一个强大的高新跟那个的代码生成包,它可以在运行期扩展java类与实现java接口,它广泛的被许多AOP的框架使用,例如 Spring AOP ,实现方法拦截
4.在AOP编程中如何选择代理模式:
1.怒表对象需要实现接口,用JDK代理
2.目标对象不需要实现接口,用Cglib代理
5.Cglib包的底层是通过使用字节码处理框架ASM来转换字节码并生成新的类
public class TeacherDao {
public void teach() {
System.out.println("老师在授课");
}
}
public class ProxyFactory implements MethodInterceptor {
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
public Object getProxyInstance() {
//创建工具类
Enhancer enhancer = new Enhancer();
//设置父类
enhancer.setSuperclass(target.getClass());
//设置回调函数
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("Cglib 代理对象开始");
Object invoke = method.invoke(target, objects);
System.out.println("Cglib 代理对象结束");
return invoke;
}
}
public static void main(String[] args) {
TeacherDao teacherDao = new TeacherDao();
ProxyFactory proxyFactory = new ProxyFactory(teacherDao);
teacherDao = (TeacherDao) proxyFactory.getProxyInstance();
teacherDao.teach();
}
代理模式的变体
1.防火墙代理
内网通过代理穿透防火墙,实现公网访问
2.缓存代理
比如:当请求图片文件等资源时,先到缓存代理取,如果取到资源则ok,如果取不到资源,再到公网或者数据库取,然后缓存
3.远程代理
远程对象的本地代表,通过它可以把远程对象当本地对象来调用。远程代理通过网络真正的远程对象沟通信息
4.同步代理
主要使用多线程编程中,完成多线程间同步工作
14.模板方法模式
基本介绍
1.模板方法模式(Trmplate Method Pattern),又叫模板模式(Template Pattern),在一个抽象类公开定义执行它的方法的模板。它的子类可以俺需要重写方法实现,但是调用讲义抽象类中定义的方式进行
2.简单说,模板方法模式定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构,就可以重定义该算法的某些特定步骤
3.这种类型的设计模式属于行为型模式
public abstract class SoyaMilk {
final void make() {
select();
addCondiments();
soak();
beat();
}
void select() {
System.out.println("选择新鲜的黄豆");
}
public abstract void addCondiments();
void soak() {
System.out.println("第三步,黄豆和配料浸泡一段时间");
}
void beat() {
System.out.println("第四部:黄豆和配料放在一起打碎");
}
}
public class PeanutSoyaMilk extends SoyaMilk{
@Override
public void addCondiments() {
System.out.println("加入配料花生");
}
}
public class BlackBeanSoyaMilk extends SoyaMilk{
@Override
public void addCondiments() {
System.out.println("加入配料红豆");
}
}
public static void main(String[] args) {
SoyaMilk soyaMilk = new BlackBeanSoyaMilk();
soyaMilk.make();
SoyaMilk soyaMilk1 = new PeanutSoyaMilk();
soyaMilk1.make();
}
模板方法的钩子方法
1.在模板方法模式的父类中,我们可以定义一个方法,它默认不做任何事,子类可以视情况要不要覆盖它,该方法称为“钩子”
public abstract class SoyaMilk {
final void make() {
select();
if (customerWantCondiments()) {
addCondiments();
}
soak();
beat();
}
void select() {
System.out.println("选择新鲜的黄豆");
}
public abstract void addCondiments();
void soak() {
System.out.println("第三步,黄豆和配料浸泡一段时间");
}
void beat() {
System.out.println("第四部:黄豆和配料放在一起打碎");
}
//钩子方法
boolean customerWantCondiments() {
return true;
}
}
public class PureSoyaMilk extends SoyaMilk{
@Override
public void addCondiments() {
}
@Override
boolean customerWantCondiments() {
return false;
}
}
public class PeanutSoyaMilk extends SoyaMilk {
@Override
public void addCondiments() {
System.out.println("加入配料花生");
}
}
public class BlackBeanSoyaMilk extends SoyaMilk {
@Override
public void addCondiments() {
System.out.println("加入配料红豆");
}
}
public static void main(String[] args) {
SoyaMilk soyaMilk = new BlackBeanSoyaMilk();
soyaMilk.make();
SoyaMilk soyaMilk1 = new PeanutSoyaMilk();
soyaMilk1.make();
SoyaMilk soyaMilk2 = new PureSoyaMilk();
soyaMilk2.make();
}
15.命令模式
基本介绍
1.命令模式(Command Pattern):在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接受者是谁,也不知道被请求的操作是哪个
我们只需要在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来进行设计
2.命令模式使得请求发送者与请求接收者消除彼此之间的耦合,让对象之间的调用关系更灵活,实现解耦
3.在命令模式中,会将一个请求封装为一个对象,以便使用不同参数来表示不同的请求(即命令),同时命令模式也支持可撤退的操作。
4.通俗易懂的理解:将军发布命令,士兵去执行。其中有几个角色:将军(命令发布者),士兵(命令的具体执行者),命令(连接将军和士兵)
Invoker是调用者(将军),Receiver是被调用者(士兵),MyCommand是命令,实现了Command接口,持有接受对象
public interface Command {
void execute();
void undo();
}
public class LightReceive {
public void on() {
System.out.println("电灯打开啦");
}
public void off() {
System.out.println("电灯关闭啦");
}
}
public class LightOnCommand implements Command{
LightReceive light;
public LightOnCommand(LightReceive light) {
this.light = light;
}
@Override
public void execute() {
light.on();
}
@Override
public void undo() {
light.off();
}
}
public class LightOffCommand implements Command{
LightReceive light;
public LightOffCommand(LightReceive light) {
this.light = light;
}
@Override
public void execute() {
light.off();
}
@Override
public void undo() {
light.on();
}
}
public class NoCommand implements Command{
@Override
public void execute() {
}
@Override
public void undo() {
}
}
public class TVReceive {
public void on() {
System.out.println("电视机打开啦");
}
public void off() {
System.out.println("电视机关闭啦");
}
}
public class TVOffCommand implements Command{
TVReceive receive;
public TVOffCommand(TVReceive receive) {
this.receive = receive;
}
@Override
public void execute() {
receive.off();
}
@Override
public void undo() {
receive.on();
}
}
public class TVOnCommand implements Command{
TVReceive receive;
public TVOnCommand(TVReceive receive) {
this.receive = receive;
}
@Override
public void execute() {
receive.on();
}
@Override
public void undo() {
receive.off();
}
}
public class RemoteController {
Command onCommands[];
Command offCommands[];
Command undoCommand;
public RemoteController() {
onCommands = new Command[5];
offCommands = new Command[5];
for (int i = 0; i < onCommands.length; i++) {
onCommands[i] = new NoCommand();
offCommands[i] = new NoCommand();
}
}
public void setCommand(int no,Command onCommand,Command offCommand) {
onCommands[no] = onCommand;
offCommands[no] = offCommand;
}
public void onButtonWasPushed(int no) {
onCommands[no].execute();
undoCommand = onCommands[no];
}
public void offButtonWasPushed(int no) {
offCommands[no].execute();
undoCommand = offCommands[no];
}
public void undoButtonWasPushed() {
undoCommand.undo();
}
}
public static void main(String[] args) {
LightReceive receive = new LightReceive();
LightOnCommand onCommand = new LightOnCommand(receive);
LightOffCommand offCommand = new LightOffCommand(receive);
RemoteController remoteController = new RemoteController();
remoteController.setCommand(0,onCommand,offCommand);
System.out.println("__________________");
remoteController.onButtonWasPushed(0);
remoteController.offButtonWasPushed(0);
remoteController.undoButtonWasPushed();
System.out.println("_______________");
TVReceive tvReceive = new TVReceive();
TVOnCommand tvOnCommand = new TVOnCommand(tvReceive);
TVOffCommand tvOffCommand = new TVOffCommand(tvReceive);
remoteController.setCommand(1,tvOnCommand,tvOffCommand);
remoteController.onButtonWasPushed(1);
remoteController.offButtonWasPushed(1);
remoteController.undoButtonWasPushed();
}
注意事项
1.将发起请求的对象与执行请求的对象解耦。发起请求的对象是调用者,调用者只要调用命令对象的execute()方法就可以让接受者工作,而不必知道具体的接收者对象是谁,是如何实现的,命令对象会负责让接收者执行请求的动作,也就是说:“请求发起者”和“请求执行者”之间的解耦是通过命令对象实现的,命令对象起到了纽带桥梁的作用
2.容易设计一个命令队列。只要把命令对象放到列队,就可以多线程的执行命令
3.容易实现队请求的撤销和重做
4.命令模式不足:可能导致谋者系统有过多的具体命令类,增加了系统的负责度,这点在使用的时候要注意
5.空命令也是一种设计模式,它为我们省去了判空的操作。在上面的实例猴子那个,如果没有空命令,我们没按下一个按键都要判空,这给我们的编码带来一定的麻烦
6.命令模式经典的应用场景:界面的按钮都是一条命令,模拟CMD(DOS命令),订单的撤销/恢复,触发-反馈机制
17.访问者模式
基本介绍
1.访问者模式(Visitor Pattern),封装一些作用于某种数据结构的各元素的操作,它可以在不改变数据结构的前提下定义作用与这些元素的新的操作。
2.主要将数据结构与数据操作分离,解决数据结构和操作耦合性问题
3.访问者模式的基本工作原理是:在访问的类里面加一个提供借贷访问者的接口
4.访问者模式主要应用场景是:需要对一个对象结构中的对象进行很多不同操作(这些操作彼此没有关联),同时需要避免让这些操作“污染”这些对象的类,可以选用访问者模式
public abstract class Action {
public abstract void getManResult(Man man);
public abstract void getWomanResult(Woman woman);
}
public class Success extends Action{
@Override
public void getManResult(Man man) {
System.out.println("男人给的评价是很成功");
}
@Override
public void getWomanResult(Woman woman) {
System.out.println("女人评价的是成功");
}
}
public class Wait extends Action{
@Override
public void getManResult(Man man) {
System.out.println("男人待定");
}
@Override
public void getWomanResult(Woman woman) {
System.out.println("女人待定");
}
}
public class Fail extends Action{
@Override
public void getManResult(Man man) {
System.out.println("男人评价失败");
}
@Override
public void getWomanResult(Woman woman) {
System.out.println("女人评价失败");
}
}
public abstract class Person {
public abstract void accept(Action action);
}
public class Man extends Person{
@Override
public void accept(Action action) {
action.getManResult(this);
}
}
public class Woman extends Person{
@Override
public void accept(Action action) {
action.getWomanResult(this);
}
}
public class ObjectStructure {
private List<Person> persons = new LinkedList<>();
public void attach(Person person) {
persons.add(person);
}
public void detach(Person person) {
persons.remove(person);
}
public void display(Action action) {
persons.forEach(person -> {
person.accept(action);
});
}
}
public static void main(String[] args) {
ObjectStructure structure = new ObjectStructure();
structure.attach(new Man());
structure.attach(new Woman());
Success success = new Success();
structure.display(success);
Fail fail = new Fail();
structure.display(fail);
Wait wait = new Wait();
structure.display(wait);
}
注意事项
优点:
1.访问者模式符合单一职责原则,让程序具有优秀的扩展性,灵活性非常高。
2.访问者模式可以对功能进行统一,可以做报表,UI,拦截器与过滤器,适用于数据结构相对稳定上午系统
缺点
1.具体元素对访问者公布细节,也就是说访问者关注了其他类的内部细节,这时迪米特尔法则所不建议的,这样造成了具体元素变更比较困难
2.违背了依赖倒转原则。访问者模式依赖的是具体元素,而不是抽象元素
3.因此,如果一个系统有比较稳定的数据结构,又有经常变化的功能需求,那么访问者模式就是比较适合
18.迭代器模式
基本介绍
1.迭代器模式(Iterator Pattern) 是常用的设计模式,属于行为型设计模式
2.如果我们的集合元素是用不同的方式实现的,有数组,还有java的集合类,或者还有其他方式,当客户端要遍历这些元素的时候就要使用多种遍历方式,而且还会暴露元素的内部结构,可以考虑使用迭代器模式解决。
3.迭代器模式,提供一种遍历集合元素的统一接口,用一致的方法遍历集合元素,不需要知道集合对象的底层表示,即:不暴露其内部的结构
public class Department {
private String name;
private String des;
public Department() {
}
public Department(String name, String des) {
this.name = name;
this.des = des;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDes() {
return des;
}
public void setDes(String des) {
this.des = des;
}
}
public class ComputerCollegeIterator implements Iterator<Department> {
Department department[];
int position = 0;
public ComputerCollegeIterator(Department[] department) {
this.department = department;
}
@Override
public boolean hasNext() {
return position < department.length && department[position] != null;
}
@Override
public Department next() {
Department departments = department[position];
position++;
return departments;
}
@Override
public void remove() {
Iterator.super.remove();
}
}
public class InfoCollegeIterator implements Iterator<Department> {
List<Department> departments;
int index = -1;
public InfoCollegeIterator(List<Department> departments) {
this.departments = departments;
}
@Override
public boolean hasNext() {
if (index >= departments.size()-1) {
return false;
} else {
index++;
return true;
}
}
@Override
public Department next() {
return departments.get(index);
}
}
public interface College {
public void getName();
public void addDepartment(String name,String des);
public Iterator<Department> createIterator();
}
public class ComputerCollege implements College{
Department departments[];
int num = 0;
public ComputerCollege() {
departments = new Department[5];
addDepartment("java","java");
addDepartment("php","php");
addDepartment("大数据","大数据");
}
@Override
public void getName() {
System.out.println("计算机学院");
}
@Override
public void addDepartment(String name, String des) {
Department department = new Department(name, des);
departments[num] = department;
num++;
}
@Override
public Iterator<Department> createIterator() {
return new ComputerCollegeIterator(departments);
}
}
public class InfoCollege implements College{
List<Department> departments = new ArrayList<>(5);
public InfoCollege() {
addDepartment("信息安全","信息安全");
addDepartment("网络安全","网络安全");
addDepartment("服务器安全","服务器安全");
}
@Override
public void getName() {
System.out.println("信息工程学院");
}
@Override
public void addDepartment(String name, String des) {
Department department = new Department(name, des);
departments.add(department);
}
@Override
public Iterator<Department> createIterator() {
return new InfoCollegeIterator(departments);
}
}
public class OutPutImpl {
List<College> collegeList;
public OutPutImpl(List<College> collegeList) {
this.collegeList = collegeList;
}
public void printCollege() {
Iterator<College> iterator = collegeList.iterator();
while (iterator.hasNext()) {
College next = iterator.next();
next.getName();
printDepartment(next.createIterator());
}
}
public void printDepartment(Iterator<Department> iterator) {
while (iterator.hasNext()) {
Department next = iterator.next();
System.out.println(next.getName());
}
}
}
public static void main(String[] args) {
List<College> collegeList = new ArrayList<>();
collegeList.add(new ComputerCollege());
collegeList.add(new InfoCollege());
new OutPutImpl(collegeList).printCollege();
}
注意事项
优点:
1.提供一个统一的方法遍历对象,客户不用在考虑聚合的类型,使用一种方法就可以年里对象了。
2.隐藏了聚合的内部结构,客户端要遍历聚合的时候只能取到迭代器,而不会知道聚合的具体组成
3.提供了一种设计思想,就是一个类应该只有一个引用变化的原因(叫做单一责任原则)。在聚合类中,我们被迭代器分开,就是要把管理对象集合和遍历对象集合责任分开,这样一来集合改变的话。只影响到聚合的对象。而如果遍历方式改变的话,只影响到了迭代器
4.当要展示一组相似对象,或者遍历一组相同对象时使用,适合使用迭代器模式
缺点:
1.每一个对象都要一个迭代器,会生成多个大迭代器不好管理类
19.观察者模式
public class CurrentConditions {
private float temperature;
private float pressure;
private float humidity;
public void update(float temperature,float pressure,float humidity) {
this.humidity = humidity;
this.pressure = pressure;
this.temperature = temperature;
display();
}
private void display() {
System.out.println("————————" + temperature);
System.out.println("————————" + pressure);
System.out.println("————————" + humidity);
}
}
public class WeatherData {
private float temperature;
private float pressure;
private float humidity;
private CurrentConditions currentConditions;
public WeatherData(CurrentConditions currentConditions) {
this.currentConditions = currentConditions;
}
public float getTemperature() {
return temperature;
}
public float getPressure() {
return pressure;
}
public float getHumidity() {
return humidity;
}
public void dataChange() {
currentConditions.update(temperature,pressure,humidity);
}
public void setData(float temperature,float humidity,float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
dataChange();
}
}
public static void main(String[] args) {
CurrentConditions currentConditions = new CurrentConditions();
WeatherData weatherData = new WeatherData(currentConditions);
weatherData.setData(30,150,100);
}
传统模式问题分析:
1.其他第三方介入气象站或区域数据的问题
2.无法在运动态的添加第三方
public interface Subject {
public void registerObServer(ObServer o);
public void removeObServer(ObServer o);
public void notifyObServer();
}
public interface ObServer {
public void update(float temperature,float pressure,float humidity);
}
public class WeatherData implements Subject{
private float temperature;
private float pressure;
private float humidity;
private final List<ObServer> obServerList;
public WeatherData() {
this.obServerList = new ArrayList<>();
}
@Override
public void registerObServer(ObServer o) {
obServerList.add(o);
}
@Override
public void removeObServer(ObServer o) {
obServerList.remove(o);
}
@Override
public void notifyObServer() {
for (ObServer obServer : obServerList) {
obServer.update(temperature,pressure,humidity);
}
}
public void setData(float temperature,float humidity,float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
notifyObServer();
}
}
public class CurrentConditions implements ObServer{
private float temperature;
private float pressure;
private float humidity;
@Override
public void update(float temperature, float pressure, float humidity) {
this.humidity = humidity;
this.pressure = pressure;
this.temperature = temperature;
display();
}
private void display() {
System.out.println("————————" + temperature);
System.out.println("————————" + pressure);
System.out.println("————————" + humidity);
}
}
public static void main(String[] args) {
WeatherData subject = new WeatherData();
CurrentConditions currentConditions = new CurrentConditions();
subject.registerObServer(currentConditions);
subject.notifyObServer();
subject.setData(30,150,20);
}
观察者模式的好处
1.观察者模式设计后,会以集合的方式来管理用户,包括注册,移除和通知
2.这样,我们增加观察者,就不需要去修改核心类WeaterData不会修改代码遵守了ocp原则
20.中介者模式
基本介绍
1.中介者模式(Mediator Pattern) ,用一个中介对象来封装各个对象不需要显式地相互吸引,从而使其耦合松散,而且可以独立地改变它们之间的交互
2.中介者模式属于行为型模式,是的代码易于维护
3.比如MVC模式,C(controller控制器)是M(Model mol 模型)和V(View视图)的中介者,在前后端交互起了中间人的作用
注意事项
1.多个类相互耦合,会形成网状结构,使用中介者模式将网状结构分离为星型结构进行解耦
2.减少类间的依赖,降低了耦合,符合迪米特尔原则
3.中介者承担了较多的责任,一旦中介出现了问题,整个系统就会受到影响
4.如果设计不当,中介者模式对象本身边的过于复杂,这点在实际使用时,要特别注意
21.备忘录模式
1.备忘录模式(Memento Pattern)在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原来的保存状态
2.可以这样理解备忘录模式:实现生活中的备忘录是用来记录某些要去做的事情,或者是记录已经达成的共同意识,以防忘记了。在软件层面,备忘录模式有这相同的含义,备忘录对象主要用来记录一个对象的某种状态,或者某些数据,当要左回退时,可以从备忘录对象里获取原来的数据进行回复操作
3.备忘录模式属于行为模式
public class Originator {
private String state;
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public Memento saveStateMemento() {
return new Memento(state);
}
public void getStateMemento(Memento memento) {
this.state = memento.getState();
}
}
public class Memento {
private String state;
public Memento(String state) {
this.state = state;
}
public String getState() {
return state;
}
}
public class Caretaker {
private List<Memento> mementos = new ArrayList<>();
public void add(Memento memento) {
mementos.add(memento);
}
public Memento get(int index) {
return mementos.get(index);
}
}
public static void main(String[] args) {
Originator originator = new Originator();
Caretaker caretaker = new Caretaker();
originator.setState("状态一");
caretaker.add(originator.saveStateMemento());
originator.setState("状态二");
caretaker.add(originator.saveStateMemento());
originator.setState("状态三");
caretaker.add(originator.saveStateMemento());
System.out.println("当前的状态" + originator.getState());
originator.getStateMemento(caretaker.get(0));
System.out.println("恢复后状态" + originator.getState());
}
public class Memento {
private int vit;
private int def;
public Memento(int vit, int def) {
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;
}
}
public class Caretaker {
private Memento memento;
//private List<Memento> mementos = new ArrayList<>();
//
//private Map<String,List<Memento>> listMap = new HashMap<>();
public Memento getMemento() {
return memento;
}
public void setMemento(Memento memento) {
this.memento = memento;
}
}
public class GameRole {
private int vit;
private int def;
public Memento createMemento() {
return new Memento(vit,def);
}
public void recoverGameRoleFormMemento(Memento memento) {
this.vit = memento.getVit();
this.def = memento.getDef();
}
public void display() {
System.out.println("攻击力" + this.vit);
System.out.println("防御力" + 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;
}
}
public static void main(String[] args) {
GameRole gameRole = new GameRole();
gameRole.setVit(100);
gameRole.setDef(100);
System.out.println("大战前的状态");
gameRole.display();
Caretaker caretaker = new Caretaker();
caretaker.setMemento(new Memento(gameRole.getVit(), gameRole.getDef()));
System.out.println("大战");
gameRole.setVit(30);
gameRole.setDef(30);
gameRole.display();
System.out.println("回复");
gameRole.recoverGameRoleFormMemento(caretaker.getMemento());
gameRole.display();
}
注意事项
1.给用户提供了一种可以恢复状态的机制,可以使用户能够方便地回到某个历史的状态
2.实现了信息的封装,使得用户不需要关心状态的保存细节
3.如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存,这个需要注意
4.使用的应用场景:1.后悔药,2.打游戏的存档,3.windows里的ctri+z,4.ie中的后退,4.数据库的食物管理
5.为了节约内存,备忘录模式可以和原型模式配合使用
22.解释器模式
基本介绍
- 在编译原理中,一个算术表达式通过司法分析器形成词法单元,而后这些词法单元在通过语法分析器构建语法分析树,最终形成一颗抽象的语法分析树。这里的词法分析器和语法分析器都可以看做的解析器
- 解释器模式(Interpreter Pattren):是指给定一个语言(表达式),定义它的文法的一种表示,并定义一个解释器,使用该解释器来解释语言中的句子(表达式)
- 应用场景:
-
- 应用可以将一个需要解释执行的语言中的句子表示为一个抽象语法树
- 一些重复出现的问题可以用一种简单的语言来表示
- 一个简单语法需要解释的场景
- 应用可以将一个需要解释执行的语言中的句子表示为一个抽象语法树
- 这样的例子还有,比如解释器,运算表达式计算,正则表达式,机器人等。
public abstract class Expression {
public abstract int interpreter(Map<String,Integer> var);
}
public class VarExpression extends Expression{
private String key;
public VarExpression(String key) {
this.key = key;
}
public String getKey() {
return key;
}
@Override
public int interpreter(Map<String, Integer> var) {
return var.get(this.key);
}
}
public class SymbolExpression extends Expression{
protected Expression left;
protected Expression right;
public SymbolExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
@Override
public int interpreter(Map<String, Integer> var) {
return 0;
}
}
public class SubExpression extends SymbolExpression{
public SubExpression(Expression left, Expression right) {
super(left, right);
}
@Override
public int interpreter(Map<String, Integer> var) {
return super.left.interpreter(var) - super.right.interpreter(var);
}
}
public class AddExpression extends SymbolExpression{
public AddExpression(Expression left, Expression right) {
super(left, right);
}
@Override
public int interpreter(Map<String, Integer> var) {
return super.left.interpreter(var) + super.right.interpreter(var);
}
}
public class Calculator {
private Expression expression;
public Calculator(String expStr) {
Stack<Expression> stack = new Stack<>();
char[] charArray = expStr.toCharArray();
Expression left = null;
Expression right = null;
for (int i = 0; i < charArray.length; i++) {
switch (charArray[i]){
case '+':
left = stack.pop();
right = new VarExpression(String.valueOf(charArray[++i]));
stack.push(new AddExpression(left,right));
break;
case '-':
left = stack.pop();
right = new VarExpression(String.valueOf(charArray[++i]));
stack.push(new AddExpression(left,right));
break;
default:
stack.push(new VarExpression(String.valueOf(charArray[i])));
break;
}
}
expression = stack.pop();
}
public int run(Map<String,Integer> var) {
return this.expression.interpreter(var);
}
}
private static final Scanner scanner = new Scanner(System.in);
public static void main(String[] args) {
System.out.println("请输入表达式:");
String expStr = scanner.nextLine();
Map<String,Integer> var = getValue(expStr);
Calculator calculator = new Calculator(expStr);
System.out.println("运算结果:" + calculator.run(var));
}
private static Map<String, Integer> getValue(String expStr) {
Map<String,Integer> map = new HashMap<>();
for (char ch : expStr.toCharArray()) {
if (ch != '+' && ch != '-') {
if (!map.containsKey(String.valueOf(ch))) {
System.out.println("请输入" + ch + "的值");
int value = scanner.nextInt();
map.put(String.valueOf(ch),value);
}
}
}
return map;
}
注意事项
- 当有一个语言需要解释执行,可将该语言中的句子表示为一个抽象语法树,就可以考虑使用解释器模式,让程序具有良好的扩展性
- 应用场景:编译器,运算表达式计算,正则表达式,机器人等
- 使用解释器可能带来的问题:解释器模式会引起类膨胀,解释器模式采用递归电泳方式,将会导致调用非常复杂,效率可能降低
23.状态模式
- 状态模式(State Pattern):它主要用来解决对象在多种状态转换时,需要对外输出不同的行为的问题。状态和行为是一一对应的,状态之间可以相互转换
- 当一个对象的内在状态改变时,允许改变其行为,这个对象看起来像是改变了其类
public abstract class State {
public abstract void deductMoney();
public abstract Boolean raffle();
public abstract void dispensePrize();
}
public class RaffleActivity {
State state = null;
int count = 0;
State noRaffleSate = new NoRaffleSate(this);
State canRaffleState = new CanRaffleState(this);
State dispenseState = new DispenseState(this);
State dispenseOutState = new DispenseOutState(this);
public RaffleActivity(int count) {
this.state = getNoRaffleSate();
this.count = count;
}
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;
}
public int getCount() {
int curCount = count;
count--;
return curCount;
}
public void setCount(int count) {
this.count = count;
}
public State getNoRaffleSate() {
return noRaffleSate;
}
public void setNoRaffleSate(State noRaffleSate) {
this.noRaffleSate = noRaffleSate;
}
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 getDispenseOutState() {
return dispenseOutState;
}
public void setDispenseOutState(State dispenseOutState) {
this.dispenseOutState = dispenseOutState;
}
}
public class NoRaffleSate extends State{
RaffleActivity activity;
public NoRaffleSate(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("不能发放奖品 ");
}
}
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.getNoRaffleSate());
} else {
System.out.println("很遗憾,奖品发完了");
activity.setState(activity.getDispenseOutState());
}
}
}
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("奖品发放完毕");
}
}
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 random = new Random();
int num = random.nextInt(10);
if (num == 0) {
activity.setState(activity.getDispenseState());
return true;
} else {
System.out.println("很遗憾,没有中奖");
activity.setState(activity.getNoRaffleSate());
return false;
}
}
@Override
public void dispensePrize() {
System.out.println("没有中奖不能发放奖品");
}
}
public static void main(String[] args) throws InterruptedException {
RaffleActivity raffleActivity = new RaffleActivity(1);
for (int i = 0; i < 300; i++) {
System.out.println("=======" + (i+1)+ "次抽奖=========");
raffleActivity.debuctMoney();
raffleActivity.raffle();
TimeUnit.SECONDS.sleep(1);
}
}
注意事项
- 代码有很强的可读性。状态模式将每个状态的行为封装到对应的一个类中
- 方便维护。将容易产生问题的if-else语句删除了,如果把每个状态的行为都放到一个类中,每次调用方法时都要判断当前是什么状态,不但会产生很多if-else语句,容易出错
- 符合“开闭原则”。容易增删状态
- 会产生很多类。每个状态都要一个对应的类,当状态过多时会产生很多类,加大维护难度
- 当一个事件或者对象有很多种状态,状态之间会相互转换,对不同的状态要求有不同得行为的时候,可以考虑使用状态模式
24.策略模式
基本介绍
- 策略模式(Strategy Pattern)中,定义算法族,分别封装起来,让他们之间可以互相替换,此模式让算法得到变化独立于使用算法的用户
- 这个算法体现了几个设计原则:
1. 把变化的代码从不变的代码中分离出来
2. 针对接口编程而不是具体类(定义了策略接口)
3. 多用组合、聚合,少用继承(客户通过组合方式使用策略)
public interface FlyBehavior {
public void fly();
}
public class GoodFlyBehavior implements FlyBehavior{
@Override
public void fly() {
System.out.println("飞翔技术高超");
}
}
public class BadFlyBehavior implements FlyBehavior{
@Override
public void fly() {
System.out.println("飞翔技术一般");
}
}
public class NoFlyBehavior implements FlyBehavior{
@Override
public void fly() {
System.out.println("不会飞翔");
}
}
public abstract class Duck {
FlyBehavior flyBehavior;
public abstract void display();
public void quack() {
System.out.println("鸭子嘎嘎嘎叫");
}
public void swim() {
System.out.println("鸭子会游泳");
}
public void fly() {
if (flyBehavior != null) {
flyBehavior.fly();
}
}
}
public class WildDuck extends Duck {
public WildDuck() {
flyBehavior = new GoodFlyBehavior();
}
@Override
public void display() {
System.out.println("这时野鸭子");
}
}
public class ToyDuck extends Duck {
public ToyDuck() {
flyBehavior = new NoFlyBehavior();
}
@Override
public void display() {
System.out.println("玩具鸭");
}
@Override
public void quack() {
System.out.println("玩具鸭不会叫");
}
@Override
public void swim() {
System.out.println("玩具鸭不会游泳");
}
}
public class PeKingDuck extends Duck {
public PeKingDuck() {
flyBehavior = new BadFlyBehavior();
}
@Override
public void display() {
System.out.println("北京鸭子");
}
}
注意事项
- 策略模式的关键是:分析项目中的变化部分与不变化部分
- 策略模式的核心思想是:多用组合/聚合 少用继承;用行为类组合,而不是行为继承。更有弹性
- 体现了“对修改关闭,对扩展开放”原则,客户端增加行为不用膝盖原有代码,值要添加一种策略(或者行为)即可,避免了使用多重转移语句
- 提供了可以替换继承关机的办法:策略模式将算法封装在独立的Strategy类中使得你可以独立于其Context改变它,是它易于切换,易于理解,易于扩展
- 需要注意的是:每添加一个策略就要增加一个类,当策略过多是会导致类数目庞大
25.职责链模式
基本介绍
- 职责链模式(Chain of Responsibility Pattern)又叫责任链模式,为请求创建一个接收者对象的链(简单的示意图)。这种模式队请求的发送者和接收者进行解耦
- 责任链模式通常每个接收者对象都包含对另一个接收者对象引用。如果一个对象不能处理该请求。那么他会把相同的请求传给写一个接收者,以此类推
- 这种类型的设计模式属于行为型模式
public abstract class Approver {
Approver approver;//下一个处理者
String name;
public Approver(String name) {
this.name = name;
}
public void setApprover(Approver approver) {
this.approver = approver;
}
//处理审批请求的方法
public abstract void processRequest(PurchaseRequest purchaseRequest);
}
public class CollegeApprover extends Approver{
public CollegeApprover(String name) {
super(name);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
if (purchaseRequest.getPrice() > 5000 && purchaseRequest.getPrice() <= 10000) {
System.out.println("请求编号" + purchaseRequest.getId() + "被" + this.name + "处理");
} else {
approver.processRequest(purchaseRequest);
}
}
}
public class DepartmentApprover extends Approver{
public DepartmentApprover(String name) {
super(name);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
if (purchaseRequest.getPrice() <= 5000) {
System.out.println("请求编号" + purchaseRequest.getId() + "被" + this.name + "处理");
} else {
approver.processRequest(purchaseRequest);
}
}
}
public class PurchaseRequest {
private Integer type = 0;
private Integer number = 0;
private Float price = 0F;
private Integer id = 0;
public PurchaseRequest(Integer type, Integer number, Float price, Integer id) {
this.type = type;
this.number = number;
this.price = price;
this.id = id;
}
public Integer getType() {
return type;
}
public Integer getNumber() {
return number;
}
public Float getPrice() {
return price;
}
public Integer getId() {
return id;
}
}
public class ShcoolMasterApprover extends Approver{
public ShcoolMasterApprover(String name) {
super(name);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
if (purchaseRequest.getPrice() > 3000) {
System.out.println("请求编号" + purchaseRequest.getId() + "被" + this.name + "处理");
} else {
approver.processRequest(purchaseRequest);
}
}
}
public class ViceShcoolMasterApprover extends Approver{
public ViceShcoolMasterApprover(String name) {
super(name);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
if (purchaseRequest.getPrice() > 10000 && purchaseRequest.getPrice() <= 30000) {
System.out.println("请求编号" + purchaseRequest.getId() + "被" + this.name + "处理");
} else {
approver.processRequest(purchaseRequest);
}
}
}
注意事项
- 将请求和处理分开,实现解耦,提高系统的灵活性
- 简化了对象,使对象不需要知道链的结构
- 性能会受到影响,特别是链比较长的时候,因此需要控制链中最大的节点数量,一般通过Handler中设置一个最大的节点数量,在setNext()方法中判断是否已经超过阈值,超过则不予许该链建立,避免出现超长链无意识第破坏系统性能
- 调试不方便。采用了类似递归的方式,调试时逻辑可能比较复杂
- 最佳应用场景:有多个对象可以处理听一个请求时,比如:多级请求,请假/加薪等审批流程,java web中tomcat对Encoding的处理,拦截器