创建型
文章很多部分内容来自此,以下为个人学习笔记,侵删
ps:图中代码实例部分 一个文件不能有多个public类,为了阅读方便都放入了同一个代码框中
单例模式
确保一个类只有一个实例,并提供该实例的全局访问点
私有构造函数保证了不能通过构造函数来创建对象实例,只能通过公有静态函数返回唯一的私有静态变量
懒汉式-线程不安全
以下实现中,私有静态变量 uniqueInstance 被延迟实例化,这样做的好处是,如果没有用到该类,那么就不会实例化 uniqueInstance,从而节约资源。
public class Singleton {
private static Singleton uniqueInstance;
private Singleton() {
}
public static Singleton getUniqueInstance() {
//线程不安全 可能同时进入
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
饿汉式-线程安全
线程不安全问题主要是由于 uniqueInstance 被实例化多次,采取直接实例化 uniqueInstance 的方式就不会产生线程不安全问题。但是直接实例化的方式也丢失了延迟实例化带来的节约资源的好处。
private static Singleton uniqueInstance = new Singleton();
懒汉式-线程安全
-
直接对方法加锁,性能较差
public static synchronized Singleton getUniqueInstance() { if (uniqueInstance == null) { uniqueInstance = new Singleton(); } return uniqueInstance; }
-
双重校验锁,保证性能的同时又实现了线程安全
public class Singleton {
private volatile static Singleton uniqueInstance;
private Singleton() {
}
public static Singleton getUniqueInstance() {
if (uniqueInstance == null) {
synchronized (Singleton.class) {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
volatile保证可见性
静态内部类实现
经过测试,静态内部类和普通内部类一样,都是只有当需要用到的时候才进行加载。
当 Singleton 类被加载时,静态内部类 SingletonHolder 没有被加载进内存。只有当调用 getUniqueInstance()方法从而触发 SingletonHolder.INSTANCE 时 SingletonHolder 才会被加载,此时初始化 INSTANCE 实例,并且JVM 能确保 INSTANCE 只被实例化一次。
这种方式不仅具有延迟初始化的好处,而且由 JVM 提供了对线程安全的支持。
public class Singleton {
private Singleton() {
}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getUniqueInstance() {
return SingletonHolder.INSTANCE;
}
}
枚举类实现
public enum SingletonEnum {
instance;
private String objName;
public String getObjName() {
return objName;
}
public void setObjName(String objName) {
this.objName = objName;
}
public static void main(String[] args) {
SingletonEnum firstSingleton=SingletonEnum.instance;
firstSingleton.setObjName("firstName");
System.out.println(firstSingleton.getObjName());
SingletonEnum secondSingleton=SingletonEnum.instance;
secondSingleton.setObjName("secondName");
System.out.println("firstSingleton = " + firstSingleton.getObjName());
System.out.println(secondSingleton.getObjName());
//反射获取实例测试
try {
SingletonEnum[] enums=SingletonEnum.class.getEnumConstants();
for (SingletonEnum singletonEnum:enums
) {
System.out.println(singletonEnum.getObjName());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
输出:
firstName
firstSingleton = secondName
secondName
secondName
该实现可以防止反射攻击。在其它实现中,通过 setAccessible() 方法可以将私有构造函数的访问级别设置为public,然后调用构造函数从而实例化对象,如果要防止这种攻击,需要在构造函数中添加防止多次实例化的代码。
该实现是由 JVM 保证只会实例化一次,因此不会出现上述的反射攻击。该实现在多次序列化和序列化之后,不会得到多个实例。而其它实现需要使用 transient 修饰所有字段,并且实现序列化和反序列化的方法。这与enum的性质有关,详细可以查看 java 枚举(enum) 全面解读
好处:可以防止反射攻击,在enum变量中,成员变量instance是一个SingletonEnum的类继承了Enum,并且被声明为static final,不可继承,而且保证单一性,可以防止反射调用私有构造函数,可以防止序列化和反序列化造成对象有多个。
缺点:枚举类会自动分配空间,枚举类的装载和初始化时会有时间和空间的成本. 它的实现比其他方式需要更多的内存空间,所以在Android这种受资源约束的设备中尽量避免使用枚举单例。
enum几点总结:
-
public enum Color { BLACK, WHITE }
反编译后会发现
// final修饰,无法被继承 public final class Color extends Enum { // 为了避免 返回的数组修改,而引起内部values值的改变,返回的是原数组的副本 public static Color[] values() { return (Color[]) $VALUES.clone(); } // 按名字获取枚举实例 public static Color valueOf(String name) { return (Color) Enum.valueOf(em / Color, name); } // 私有的构造函数 private Color(String name, int ordinal) { super(name, ordinal); } // enum第一行的声明的变量,都对应一个枚举实例对象 public static final Color BLACK; public static final Color WHITE; // private static final Color $VALUES[]; // 静态域初始化,说明在类加载的cinit阶段就会被实例化,jvm能够保证类加载过程的线程安全 static { BLACK = new Color("BLACK", 0); WHITE = new Color("WHITE", 1); $VALUES = (new Color[]{ BLACK, WHITE }); } }
2.对序列化进行特殊处理,防止反序列化时创建新的对象
在序列化的时候Java仅仅是将枚举对象的name属性输出到结果中,反序列化的时候则是通过Enum的
valueOf()
方法来根据名字查找枚举对象。同时,编译器是不允许任何对这种序列化进行定制,因此禁用了writeObject
、readObject
、readObjectNoData
、writeReplace
和readResolve
等方法。3.私有构造函数, 无法正常的
new
出对象4.无法通过
clone()
方法,克隆对象/** * Throws CloneNotSupportedException. This guarantees that enums * are never cloned, which is necessary to preserve their "singleton" * status. */ protected final Object clone() throws CloneNotSupportedException { throw new CloneNotSupportedException(); }
5.无法通过反射的方式创建枚举对象
枚举类型,在 JVM 层面禁止了通过反射构造枚举实例的行为,如果尝试通过反射创建,将会报
Cannot reflectively create enum objects
.枚举实例必须在
enum
关键字声明的类中显式的指定(首行开始的以第一个分号结束)除了上面, 没有任何方式(new,clone,反射,序列化)可以手动创建枚举实例
枚举类不可被继承
枚举类是线程安全的
枚举类型是类型安全的(typesafe)
(枚举类型是类型安全的,可以对传入的值进行类型检查:
如有个
handleColor(Color color)
方法,那么方法参数自动会对类型进行检查,只能传入Color.WHITE
和Color.BLACK
)无法继承其他类(已经默认继承Enum)
简单工厂
在创建一个对象时不向客户暴露内部细节,提供一个创建对象的通用接口
简单工厂把实例化的操作单独放到一个类中,这个类就成为简单工厂类,让简单工厂类来决定应该用哪个具体子类来实例化。
**这样做能把客户类和具体子类的实现解耦,**客户类不再需要知道有哪些子类以及应当实例化哪个子类。客户类往往有多个,如果不使用简单工厂,那么所有的客户类都要知道所有子类的细节。而且一旦子类发生改变,例如增加子类,那么所有的客户类都要进行修改。
总结:实现解耦,一个工厂可以创建多个产品
实现:
public interface Product {
}
public class ConcreteProduct implements Product {
}
public class ConcreteProduct1 implements Product {
}
public class ConcreteProduct2 implements Product {
}
public class SimpleFactory {
public Product createProduct(int type) {
if (type == 1) {
return new ConcreteProduct1();
} else if (type == 2) {
return new ConcreteProduct2();
}
return new ConcreteProduct();
}
}
public class Client {
public static void main(String[] args) {
SimpleFactory simpleFactory = new SimpleFactory();
Product product = simpleFactory.createProduct(1);
// do something with the product
}
}
工厂方法
定义了一个创建对象的接口,但由子类决定要实例化哪个类。工厂方法把实例化操作推迟到子类。
与上面一个类的区别是,定义了一个抽象工厂方法, 想要增加产品的时候,要增加应对的工厂,那么就可以继承抽象工厂,实现它的方法,而且在抽象工厂里可以添加所有工厂都要使用的方法。
代码实现
public abstract class Factory {
abstract public Product factoryMethod();
public void doSomething() {
Product product = factoryMethod();
// do something with the product
}
}
public class ConcreteFactory extends Factory {
public Product factoryMethod() {
return new ConcreteProduct();
}
}
public class ConcreteFactory1 extends Factory {
public Product factoryMethod() {
return new ConcreteProduct1();
}
}
public class ConcreteFactory2 extends Factory {
public Product factoryMethod() {
return new ConcreteProduct2();
}
}
抽象工厂
提供一个接口,用于创建 相关的对象家族 。
抽象工厂跟上面的工厂方法挺像的,不同的是抽象工厂一个工厂里可以创建一系列产品,比如A可以衍生出A1,A2,B可以衍生出B1,B2 工厂可以AB一起生产。
举个例子,假如A是键盘,B是鼠标,那么1表示惠普,2表示戴尔,在ConcreteFactory1里,可以同时生产A1,B1 惠普的鼠标键盘,如下图所示:
抽象工厂模式创建的是对象家族,也就是很多对象而不是一个对象,并且这些对象是相关的,也就是说必须一起创建出来。而工厂方法模式只是用于创建一个对象,这和抽象工厂模式有很大不同。
代码实现:
public class AbstractProductA {
}
public class AbstractProductB {
}
public class ProductA1 extends AbstractProductA {
}
public class ProductA2 extends AbstractProductA {
}
public class ProductB1 extends AbstractProductB {
}
public class ProductB2 extends AbstractProductB {
}
public abstract class AbstractFactory {
abstract AbstractProductA createProductA();
abstract AbstractProductB createProductB();
}
public class ConcreteFactory1 extends AbstractFactory {
AbstractProductA createProductA() {
return new ProductA1();
}
AbstractProductB createProductB() {
return new ProductB1();
}
}
public class ConcreteFactory2 extends AbstractFactory {
AbstractProductA createProductA() {
return new ProductA2();
}
AbstractProductB createProductB() {
return new ProductB2();
}
}
public class Client {
public static void main(String[] args) {
AbstractFactory abstractFactory = new ConcreteFactory1();
AbstractProductA productA = abstractFactory.createProductA();
AbstractProductB productB = abstractFactory.createProductB();
// do something with productA and productB
}
}
建造者模式(生成器模式)
封装一个对象的构造过程,并允许按步骤构造。将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
Builder就是各个不同的对象的具体构造者,Director负责指挥Builder的建造过程,客户端只需要跟指挥者交互,在客户端确定具体建造者的类型,然后传入Director就行。
案例:
某游戏软件中人物角色包括多种类型,不同类型的人物角色,其性别、脸型、服装、发型等外部特征有所差异,使用建造者模式创建人物角色对象,请根据类图编程实现该系统,并写出相应Java代码。
package lab2_1;
//Actor角色类:复合产品
class Actor
{
private String type;
private String sex;
private String face;
private String costume;
private String hairstyle;
public void setType(String type) {
this.type = type;
}
public void setSex(String sex) {
this.sex = sex;
}
public void setFace(String face) {
this.face = face;
}
public void setCostume(String costume) {
this.costume = costume;
}
public void setHairstyle(String hairstyle) {
this.hairstyle = hairstyle;
}
public String getType() {
return (this.type);
}
public String getSex() {
return (this.sex);
}
public String getFace() {
return (this.face);
}
public String getCostume() {
return (this.costume);
}
public String getHairstyle() {
return (this.hairstyle);
}
}
//角色建造器:抽象建造者
abstract class ActorBuilder
{
protected Actor actor = new Actor();
public abstract void buildType();
public abstract void buildSex();
public abstract void buildFace();
public abstract void buildCostume();
public abstract void buildHairstyle();
public Actor createActor()
{
return actor;
}
}
//英雄角色建造器:具体建造者
class HeroBuilder extends ActorBuilder
{
public void buildType()
{
actor.setType("英雄");
}
public void buildSex()
{
actor.setSex("男");
}
public void buildFace()
{
actor.setFace("英俊");
}
public void buildCostume()
{
actor.setCostume("盔甲");
}
public void buildHairstyle()
{
actor.setHairstyle("飘逸");
}
}
//天使角色建造器:具体建造者
class AngelBuilder extends ActorBuilder
{
public void buildType()
{
actor.setType("天使");
}
public void buildSex()
{
actor.setSex("女");
}
public void buildFace()
{
actor.setFace("漂亮");
}
public void buildCostume()
{
actor.setCostume("白裙");
}
public void buildHairstyle()
{
actor.setHairstyle("披肩长发");
}
}
//魔鬼角色建造器:具体建造者
class GhostBuilder extends ActorBuilder
{
public void buildType()
{
actor.setType("魔鬼");
}
public void buildSex()
{
actor.setSex("妖");
}
public void buildFace()
{
actor.setFace("丑陋");
}
public void buildCostume()
{
actor.setCostume("黑衣");
}
public void buildHairstyle()
{
actor.setHairstyle("光头");
}
}
//Actor角色创建控制器:指挥者
class ActorController
{
public Actor construct(ActorBuilder ab)
{
//传入一个构造者,然后构造各种属性,返回结果
Actor actor;
ab.buildType();
ab.buildSex();
ab.buildFace();
ab.buildCostume();
ab.buildHairstyle();
actor=ab.createActor();
return actor;
}
}
//客户端测试类
class Client
{
public static void main(String args[])
{
ActorController ac = new ActorController();
ActorBuilder ab;
ab = new AngelBuilder();
Actor angel;
angel = ac.construct(ab);
String type = angel.getType();
System.out.println(type + "的外观:");
System.out.println("性别:" + angel.getSex());
System.out.println("面容:" + angel.getFace());
System.out.println("服装:" + angel.getCostume());
System.out.println("发型:" + angel.getHairstyle());
}
}
案例2,简易StringBuilder实现
import java.util.Arrays;
public class AbstractStringBuilder {
//value数组
protected char[] value;
//记录存储的最后一个下标
protected int count;
public AbstractStringBuilder(int capacity) {
count = 0;
value = new char[capacity];
}
public AbstractStringBuilder append(char c) {
ensureCapacityInternal(count + 1);
value[count++] = c;
return this;
}
//确保容量足够
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0)
expandCapacity(minimumCapacity);
}
//扩容函数
void expandCapacity(int minimumCapacity) {
int newCapacity = value.length * 2 + 2;
if (newCapacity - minimumCapacity < 0)
newCapacity = minimumCapacity;
if (newCapacity < 0) {
if (minimumCapacity < 0) // overflow
throw new OutOfMemoryError();
newCapacity = Integer.MAX_VALUE;
}
value = Arrays.copyOf(value, newCapacity);
}
}
class StringBuilder extends AbstractStringBuilder {
public StringBuilder() {
super(16);
}
@Override
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}
}
class ClientStringBuilder {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
final int count = 26;
for (int i = 0; i < count; i++) {
sb.append((char) ('a' + i));
}
System.out.println(sb.toString());
}
}
输出:
abcdefghijklmnopqrstuvwxyz
解释:这里的构造过程可以理解为append方法,也就是类图中的buildPart()方法,而这里不存在builder哪一个类,只是追加char数组,不像上面的案例,可能是构造天使,魔鬼等不同角色,所以没有director,或者说StringBuilder本身就是director
原型模式
使用原型实例指定要创建对象的类型,通过复制这个原型来创建新对象。其实就是有个clone方法,像object类中有个clone方法
代码实现
public abstract class Prototype {
abstract Prototype myClone();
}
public class ConcretePrototype extends Prototype {
private String filed;
public ConcretePrototype(String filed) {
this.filed = filed;
}
//这个clone方法是关键
@Override
Prototype myClone() {
return new ConcretePrototype(filed);
}
@Override
public String toString() {
return filed;
}
}
public class Client {
public static void main(String[] args) {
Prototype prototype = new ConcretePrototype("abc");
Prototype clone = prototype.myClone();
System.out.println(clone.toString());
}
}
行为型
责任链
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链发送该请求,直到有一个对象处理它为止。
successor就是用来传递请求的,举个例子,加入Handler1是老师,Handler2是校长,那么handler1就可以通过这个successor传递给handler2。
- 实例1:
某公司欲开发一个软件的在线文档帮助系统,用户可以在任何一个查询环境中输入查询关键字,如果当前查询环境下没有相关的内容,则系统会将查询按照一定的顺序转发给其他查询环境。客户可以定制自己的查询顺序,例如先查询Java关键字,再查询SQL关键字,最后查询UML关键字。请根据类图编程实现该系统,并写出相应Java代码。
implementation:
/**
*
* @author:yxh
* 责任链模式实例
*/
/**
* 测试类
*/
class Client{
public static void main(String[] args) {
SearchContext jContext,sContent,uContext;
jContext=new JavaSearchContext();
sContent=new SQLSearchContext();
uContext=new UMLSearchContext();
jContext.setSuccessor(sContent);
sContent.setSuccessor(uContext);
String keyword="UML类图绘制疑惑";
jContext.search(keyword);
}
}
abstract class SearchContext{
SearchContext successor;
public void setSuccessor(SearchContext successor) {
this.successor = successor;
}
abstract void search(String keyWord);
}
/**
* Java查询类
* */
class JavaSearchContext extends SearchContext {
@Override
void search(String keyWord) {
//处理请求
if (keyWord.indexOf("Java")>=0)
System.out.println("Java关键字查询");
//转发请求
else
successor.search(keyWord);
}
}
/**
* SQL查询类
*/
class SQLSearchContext extends SearchContext {
@Override
void search(String keyWord) {
//处理请求
if (keyWord.indexOf("SQL")>=0)
System.out.println("SQL关键字查询");
//转发请求
else
successor.search(keyWord);
}
}
/**
* UML搜索类
*/
class UMLSearchContext extends SearchContext {
@Override
void search(String keyWord) {
if (keyWord.indexOf("UML")>=0)
System.out.println("UML关键字查询");
}
}
输出:
查询关键字UML
- 实例2:
此处定义了一个枚举变量,TYPE来确定是由第几个Handler处理,TYPE1就由第一个Handler处理
public abstract class Handler {
protected Handler successor;
public Handler(Handler successor) {
this.successor = successor;
}
protected abstract void handleRequest(Request request);
}
public class ConcreteHandler1 extends Handler {
public ConcreteHandler1(Handler successor) {
super(successor);
}
@Override
protected void handleRequest(Request request) {
if (request.getType() == RequestType.TYPE1) {
System.out.println(request.getName() + " is handle by ConcreteHandler1");
return;
}
if (successor != null) {
successor.handleRequest(request);
}
}
}
public class ConcreteHandler2 extends Handler {
public ConcreteHandler2(Handler successor) {
super(successor);
}
@Override
protected void handleRequest(Request request) {
if (request.getType() == RequestType.TYPE2) {
System.out.println(request.getName() + " is handle by ConcreteHandler2");
return;
}
if (successor != null) {
successor.handleRequest(request);
}
}
}
public class Request {
private RequestType type;
private String name;
public Request(RequestType type, String name) {
this.type = type;
this.name = name;
}
public RequestType getType() {
return type;
}
public String getName() {
return name;
}
}
public enum RequestType {
TYPE1, TYPE2
}
public class Client {
public static void main(String[] args) {
Handler handler1 = new ConcreteHandler1(null);
Handler handler2 = new ConcreteHandler2(handler1);
Request request1 = new Request(RequestType.TYPE1,
"request1");
handler2.handleRequest(request1);
Request request2 = new Request(RequestType.TYPE2,
"request2");
handler2.handleRequest(request2);
}
}
输出:
request1 is handle by ConcreteHandler1
request2 is handle by ConcreteHandler2
命令模式
把发送者和接收者完全解耦,发送者和接收者没有直接引用关系,发送请求的对象只需要知道如何发送请求,而不必知道如何完成请求。
-
Command和ConcreteCommand就是抽象命令和具体命令,commande里可以增加undo()方法表示撤销
-
receiver command里的execute调用receiver的action,也就是他是命令的真正执行者,
-
invoker调用命令,多个命令组成invoker,invoker调用command里的execute,
-
client只需要与invoker交互
为什么需要一个Receiver呢,如果需要增加命令,那么修改receiver的值,再增加一个ConcreteCommand就可以了,解耦。或者说结构清晰功能明确,receiver专门用来实现命令,而ConcreteCommand调用命令。
实例:
了用户使用方便,某系统提供了一系列功能键,用户可以自定义功能键的功能,如功能键FunctionButton可以用于退出系统(SystemExitClass),也可以用于打开帮助界面(DisplayHelpClass)。用户可以通过修改配置文件来改变功能键的用途,现使用命令模式来设计该系统,使得功能键类与功能类之间解耦,相同的功能键可以对应不同的功能。请根据类图编程实现该系统,并写出相应Java代码。
/**
*
* @author:yxh
* 命令模式实例
* 将命令调用者和命令请求者解耦
* 好处: 1、降低了系统耦合度。
* 2、新的命令可以很容易添加到系统中去。
* 3.命令的执行更加灵活,可以进行命令排序,记录撤销等操作
*/
/**
* 测试类
*/
class client4_3{
public static void main(String[] args) {
Command command1,command2;
command1=new ExitCommand();
command2=new HelpCommand();
FunctionButton functionButton1=new FunctionButton(command1);
FunctionButton functionButton2=new FunctionButton(command2);
functionButton1.click();
functionButton2.click();
}
}
/**
* 抽象命令类
*/
abstract class Command{
abstract void excute();
}
/**
* 命令请求类 对应receiver
*/
class SystemExitClass{
void exit(){
System.out.println("退出系统");
}
}
class DisplayHelpClass{
void display(){
System.out.println("打开帮助系统");
}
}
/**
* 命令实体类
*/
class ExitCommand extends Command{
private SystemExitClass systemExitClass=new SystemExitClass();
@Override
void excute() {
systemExitClass.exit();
}
}
class HelpCommand extends Command{
private DisplayHelpClass displayHelpClass=new DisplayHelpClass();
@Override
void excute() {
displayHelpClass.display();
}
}
/**
* 命令调用类,对应Invoker
* */
class FunctionButton{
Command command;
public FunctionButton(Command command) {
this.command = command;
}
public void setCommand(Command command) {
this.command = command;
}
public void click(){
command.excute();
}
}
解释器模式
为语言创建解释器,通常由语言的语法和语法分析来定义。
TerminalExpression:终结符表达式,每个终结符都需要一个
Context:上下文,包含解释器之外的一些全局信息。
实例:
import java.util.StringTokenizer;
public class jieshiqi {
}
abstract class Expression {
//interpret 翻译
public abstract boolean interpret(String str);
}
/**
*终结符 ,也就是比如 A OR B A和B属于终结符, OR属于非终结符 ,
*/
class TerminalExpression extends Expression {
private String literal = null;
public TerminalExpression(String str) {
literal = str;
}
@Override
public boolean interpret(String str) {
/*
* Java StringTokenizer 属于 java.util 包,用于分隔字符串。
StringTokenizer 构造方法:
1. StringTokenizer(String str) :构造一个用来解析 str 的 StringTokenizer 对象。
* java 默认的分隔符是空格("")、制表符(\t)、换行符(\n)、回车符(\r)。*/
StringTokenizer st = new StringTokenizer(str);
while (st.hasMoreTokens()) {
String test = st.nextToken();
if (test.equals(literal)) {
return true;
}
}
return false;
}
}
/*
* 这两个属于非终结符
* */
class AndExpression extends Expression {
private Expression expression1 = null;
private Expression expression2 = null;
public AndExpression(Expression expression1, Expression expression2) {
this.expression1 = expression1;
this.expression2 = expression2;
}
@Override
public boolean interpret(String str) {
return expression1.interpret(str) && expression2.interpret(str);
}
}
class OrExpression extends Expression {
private Expression expression1 = null;
private Expression expression2 = null;
public OrExpression(Expression expression1, Expression expression2) {
this.expression1 = expression1;
this.expression2 = expression2;
}
@Override
public boolean interpret(String str) {
return expression1.interpret(str) || expression2.interpret(str);
}
}
class Clientjieshiqi {
/**
* 构建解析树
*/
public static Expression buildInterpreterTree() {
// Literal
Expression terminal1 = new TerminalExpression("A");
Expression terminal2 = new TerminalExpression("B");
Expression terminal3 = new TerminalExpression("C");
Expression terminal4 = new TerminalExpression("D");
// B C
Expression alternation1 = new OrExpression(terminal2, terminal3);
// A Or (B C)
Expression alternation2 = new OrExpression(terminal1, alternation1);
// D And (A Or (B C))
return new AndExpression(terminal4, alternation2);
}
public static void main(String[] args) {
//构建一个表达式
Expression define = buildInterpreterTree();
//Context: 环境类 也可以理解为上下文,存储一些全局信息
String context1 =
"D A";
String context2 =
"A B";
//DA是否满足这个表达式
System.out.println(define.interpret(context1));
System.out.println(define.interpret(context2));
}
}
解释:
D And (A Or (B C)),构建的文法树中 express1为D epresssion2为 一个OrExpression , OrExpression里又包含一个 B OR C,只有当只包含一个终结符时,才会执行 TerminalExpression.interpret()。
迭代器模式
提供了一种访问聚合对象内部元素的方式,并且不暴露对象的内部表示细节
-
类图中的iterator可以增加一个first方法用于获取第一个元素
-
Aggregate 是聚合类,其中 createIterator() 方法可以产生一个 Iterator;
-
Iterator 主要定义了 hasNext() 和 next() 方法。
-
Client 组合了 Aggregate,为了迭代遍历 Aggregate,也需要组合 Iterator。
实例
某商品管理系统的商品名称存储在一个字符串数组中,现需要自定义一个双向迭代器(MyIterator)实现对该商品名称数组的双向(前向和后向)遍历。请根据类图编程实现该系统,并写出相应Java代码。
/**
* @author yxh
* 迭代器模式实例
*/
import java.util.ArrayList;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* 实现迭代器接口
*/
class MyIterator implements AbstractIterator {
private String[] productsName;
int index1; //nextIndex
int index2;
MyIterator(AbstractProductList list) {
productsName = list.getProductsName();
index1=0;
index2=productsName.length-1;
}
@Override
public void next() {
index1++;
if (index1 >= productsName.length + 1)
throw new IndexOutOfBoundsException();
}
@Override
public boolean isLast() {
//到达末尾
return index1 == productsName.length;
}
@Override
public void previous() {
if (index2 == -1)
throw new IndexOutOfBoundsException();
else {
index2--;
}
}
@Override
public boolean isFirst() {
return index2 == -1;
}
@Override
public String getnextItem() {
return productsName[index1];
}
@Override
public String getPreviousItem() {
if (index2 <= -1)
throw new NoSuchElementException();
return productsName[index2];
}
}
/**
* 抽象迭代器接口
*/
interface AbstractIterator {
void next();
boolean isLast();
void previous();
boolean isFirst();
String getnextItem();
String getPreviousItem();
}
/**
* 抽象商品列表类
*/
abstract class AbstractProductList {
public void setProductsName(String[] productsName) {
this.productsName = productsName;
}
private String[] productsName;
public AbstractProductList(String[] productsName) {
this.productsName = productsName;
}
public String[] getProductsName() {
return productsName;
}
public abstract AbstractIterator getIterator();
}
/**
* 商品列表类
*/
class ProductList extends AbstractProductList {
ProductList(String[] productsName) {
super(productsName);
}
@Override
public AbstractIterator getIterator() {
return new MyIterator(this);
}
}
class Client5_2 {
public static void main(String[] args) {
String[] pNames = {"ThinkPad电脑", "Tissot手表", "iPhone手机", "LV手提包"};
AbstractIterator iterator;
AbstractProductList list;
list = new ProductList(pNames);
iterator = list.getIterator();
//如果不是末尾的元素就取
while (!iterator.isLast()) {
System.out.println(iterator.getnextItem());
iterator.next();
}
System.out.println("------------------");
while (!iterator.isFirst()) {
System.out.println(iterator.getPreviousItem());
iterator.previous();
}
}
}
中介者模式
集中相关对象之间复杂的沟通和控制方式。用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
一个实例帮助理解:QQ群就是典型的中介者,假如每个QQ号互相单独沟通,对象之间的沟通就会非常复杂,如下图
类图:
Mediator:中介者,定义一个接口用于与各同事(Colleague)对象通信。
Colleague:同事,相关对象
实例
Alarm(闹钟)、CoffeePot(咖啡壶)、Calendar(日历)、Sprinkler(喷头)是一组相关的对象,在某个对象的事件产生时需要去操作其它对象,形成了下面这种依赖结构:
使用中介者模式可以将复杂的依赖结构变成星形结构:
public abstract class Colleague {
public abstract void onEvent(Mediator mediator);
}
public class Alarm extends Colleague {
@Override
//调用中介者,跟其他对象交互
public void onEvent(Mediator mediator) {
mediator.doEvent("alarm");
}
public void doAlarm() {
System.out.println("doAlarm()");
}
}
public class CoffeePot extends Colleague {
@Override
public void onEvent(Mediator mediator) {
mediator.doEvent("coffeePot");
}
public void doCoffeePot() {
System.out.println("doCoffeePot()");
}
}
public class Calender extends Colleague {
@Override
public void onEvent(Mediator mediator) {
mediator.doEvent("calender");
}
public void doCalender() {
System.out.println("doCalender()");
}
}
public class Sprinkler extends Colleague {
@Override
public void onEvent(Mediator mediator) {
mediator.doEvent("sprinkler");
}
public void doSprinkler() {
System.out.println("doSprinkler()");
}
}
public abstract class Mediator {
public abstract void doEvent(String eventType);
}
public class ConcreteMediator extends Mediator {
private Alarm alarm;
private CoffeePot coffeePot;
private Calender calender;
private Sprinkler sprinkler;
public ConcreteMediator(Alarm alarm, CoffeePot coffeePot, Calender calender, Sprinkler
sprinkler) {
this.alarm = alarm;
this.coffeePot = coffeePot;
this.calender = calender;
this.sprinkler = sprinkler;
}
//其他对象通过中介者的doEvent方法与另外的对象交互
@Override
public void doEvent(String eventType) {
//根据这个对象的类型,选择交互的顺序
switch (eventType) {
case "alarm":
doAlarmEvent();
break;
case "coffeePot":
doCoffeePotEvent();
break;
case "calender":
doCalenderEvent();
break;
default:
doSprinklerEvent();
}
}
public void doAlarmEvent() {
alarm.doAlarm();
coffeePot.doCoffeePot();
calender.doCalender();
sprinkler.doSprinkler();
}
public void doCoffeePotEvent() {
coffeePot.doCoffeePot();
alarm.doAlarm();
calender.doCalender();
sprinkler.doSprinkler();
}
public void doCalenderEvent() {
// ...
}
public void doSprinklerEvent() {
// ...
}
}
public class Client {
public static void main(String[] args) {
Alarm alarm = new Alarm();
CoffeePot coffeePot = new CoffeePot();
Calender calender = new Calender();
Sprinkler sprinkler = new Sprinkler();
Mediator mediator = new ConcreteMediator(alarm, coffeePot, calender, sprinkler);
// 闹钟事件到达,调用中介者就可以操作相关对象
alarm.onEvent(mediator);
}
}
doAlarm()
doCoffeePot()
doCalender()
doSprinkler()
备忘录模式
在不违反封装的情况下获得对象的内部状态,从而在需要时可以将对象恢复到最初状态。备份的意思
- Originator:原始对象
- Caretaker:负责人,负责保存备忘录,但是不能对备忘录的内容进行检查或者曹祖,负责人类可以存储一个或多个备忘录对象,它只负责存储对象,而不能修改对象。
- Menento:备忘录,存储原始对象的的状态。需要注意的是,只有原始对象和负责人可以与备忘录直接交互
- 举个栗子:假设Originator是我的系统,Menento是我系统的一次备份,那么caretake就是存储我备份的磁盘,我要存备份和恢复备份都需要通过Caretaker
实例
/**
* 备忘录模式实例
* 中国象棋软件,由于考虑到有些用户是“菜鸟”,经常不小心走错棋;
* 还有些用户因为不习惯使用手指在手机屏幕上拖动棋子,常常出现操作失误,
* 因此该中国象棋软件要提供“悔棋”功能,在用户走错棋或操作失误后可恢复到前一个步骤。
*
* @author yxh
* @Student Id
*/
import java.util.ArrayList;
import java.util.NoSuchElementException;
import java.util.Stack;
/**
* 象棋类
*/
class Chessman {
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
String label;
int x;
int y;
@Override
public String toString() {
return "棋子:" + label + "当前位置为" +
"第" + x + "行" +
"第" + y +
"列";
}
public Chessman(String label, int x, int y) {
this.label = label;
this.x = x;
this.y = y;
}
public ChessmanMemento save() {
ChessmanMemento chessmanMemento = new ChessmanMemento(label, x, y);
return chessmanMemento;
}
public void restore(ChessmanMemento chessmanMemento) {
this.label = chessmanMemento.getLabel();
this.x = chessmanMemento.getX();
this.y = chessmanMemento.getY();
}
}
/**
* 备忘录类
*/
class ChessmanMemento {
String label;
int x;
int y;
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public ChessmanMemento(String label, int x, int y) {
this.label = label;
this.x = x;
this.y = y;
}
}
/**
* Caretaker类 用来传递备忘录
*/
class MementoCaretaker {
private ArrayList mementolist = new ArrayList();
public ChessmanMemento getMemento(int i) {
return (ChessmanMemento) mementolist.get(i);
}
public void setMemento(ChessmanMemento memento) {
mementolist.add(memento);
}
}
class client7_1 {
private static int index = -1; //定义一个索引来记录当前状态所在位置
private static MementoCaretaker mc = new MementoCaretaker();
public static void main(String[] args) {
Chessman chessman = new Chessman("车", 1, 1);
System.out.println(chessman.toString());
//保存caretaker里
mc.setMemento(chessman.save());
index++;
chessman.setY(4);
System.out.println(chessman.toString());
mc.setMemento(chessman.save());
index++;
chessman.setX(5);
System.out.println(chessman.toString());
mc.setMemento(chessman.save());
index++;
System.out.println("---悔棋---");
chessman.restore(mc.getMemento(--index));
System.out.println(chessman.toString());
System.out.println("---悔棋---");
chessman.restore(mc.getMemento(--index));
System.out.println(chessman.toString());
System.out.println("---撤销悔棋---");
chessman.restore(mc.getMemento(++index));
System.out.println(chessman.toString());
System.out.println("---撤销悔棋---");
chessman.restore(mc.getMemento(++index));
System.out.println(chessman.toString());
}
}
看上面一个实例,chessman就是原始状态,save返回当前的状态,是一个memento对象,然后用caretaker的set方法保存这个memento对象。下次chessman用restore恢复对象,从caretaker中取备忘录然后恢复。
观察者
定义对象之间的一对多依赖,当一个对象状态改变时,它的所有依赖都会收到通知并且自动更新状态。主题(Subject)是被观察的对象,而其所有依赖者(Observer)称为观察者。
- 主题(Subject)具有注册和移除观察者、并通知所有观察者的功能,主题是通过维护一张观察者列表来实现这些操作的。
- 观察者(Observer)的注册功能需要调用主题的 registerObserver() 方法。也就是图中弄个的attach方法,功能都是一样,有的detach方法也叫remove方法
- Subject注册和移除观察者,当Subject更新后,notify观察者,并调用观察者的update方法更新观察者
实例
某在线股票软件需要提供如下功能:当股票购买者所购买的某支股票价格变化幅度达到5%时,系统将自动发送通知(包括新价格)给购买该股票的股民。现使用观察者模式设计该系统。请根据类图编程实现该系统,并写出相应Java代码。
/**
* @author yxh
* 观察者模式实例
*/
import java.util.ArrayList;
class Stock{
//该股票投资者
private ArrayList<Investor> investors=new ArrayList<>();
//该股票的名字
private String stockName;
//股票价格
private double price;
public String getStockName() {
return stockName;
}
public void setStockName(String stockName) {
this.stockName = stockName;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
//如果涨幅超过5%,就通知
if(Math.abs((this.price-price)/this.price)>=0.05)
this.notifyInvestor();
this.price = price;
}
public Stock(String stockName, double price) {
this.stockName = stockName;
this.price = price;
}
void attach(Investor investor){
investors.add(investor);
}
void detach(Investor investor)
{
investors.remove(investor);
}
void notifyInvestor(){
for (Investor investor:this.investors
) {
investor.response(this);
}
}
}
interface Investor{
void response(Stock stock);
}
class ConcreteInvestor implements Investor{
private String name;
public ConcreteInvestor(String name) {
this.name = name;
}
@Override
public void response(Stock stock) {
System.out.println("提示股民:"+this.name+"股票"+stock.getStockName()+"价格波动幅度超过5%,现在的价格是:"+stock.getPrice());
}
}
class Client6_11{
public static void main(String[] args) {
Investor investor1,investor2;
investor1=new ConcreteInvestor("杨过");
investor2=new ConcreteInvestor("小龙女");
Stock haier=new Stock("青岛海尔",20.00);
haier.attach(investor1);
haier.attach(investor2);
haier.setPrice(25.00);
}
}
该例子中,股票就是被观察对象,而投资者就是观察者,当股票价格波动超过一定范围会通知所以投资者,response可以理解股民其更新状态。
状态模式
可以看到Context包含state,不同的状态有不同的处理,所以调用state.handle;这也是java多态的一种体现
实例
在某银行系统中,我们定义了账户的三种状态:
(1) 如果账户中余额大于等于0,则账户的状态为绿色状态(GreenState),此时用户既可以向该账户存款也可以从该账户取款;
(2) 如果账户中余额小于0,并且大于等于-1000,则账户的状态为黄色状态(YellowState),此时用户既可以向该账户存款也可以从该账户取款;
(3) 如果账户中余额小于-1000,那么账户的状态为红色状态(RedState),此时用户只能向该账户存款,不能再从中取款;
现用状态模式来实现状态的转化问题,用户只需执行简单的存款和取款操作,系统根据余额数量自动转换到相应的状态。请根据类图编程实现该系统,并写出相应Java代码。
/*
* 账户类
* */
class Account{
AccountState state;
String owner;
Account(String owner,double init) {
this.owner =owner;
this.state = new GreenState(init,this);
System.out.println(this.owner +"开户,初始金额为:" +init);
System.out.println("**************************************");
}
public void setState(AccountState state) {
this.state = state;
}
public void deposit(double amount){
System.out.println(this.owner + "存款"+amount + "元");
state.deposit(amount);
System.out.println("现在余额为"+state.balance);
System.out.println("现在账户状态为" + state.getClass().getName());
System.out.println("**************************************");
}
public void withdraw(double amount){
System.out.println(this.owner +"取款" +amount);
state.withdraw(amount);
System.out.println("现在余额为" +state.balance);
System.out.println("现在账户状态为" + state.getClass().getName());
System.out.println("**************************************");
}
}
abstract class AccountState{
protected Account acc;
protected double balance;
public abstract void deposit(double amount);
public abstract void withdraw(double amount);
public abstract void stateCheck();
}
class YellowState extends AccountState{
YellowState(double balance,Account account){
this.balance=balance;
this.acc=account;
}
@Override
public void deposit(double amount) {
balance+=amount;
stateCheck();
}
@Override
public void withdraw(double amount) {
balance-=amount;
stateCheck();
}
@Override
public void stateCheck() {
if(balance>=0)
acc.state=new GreenState(balance,acc);
if(balance<-1000)
acc.state=new YellowState(balance,acc);
}
}
class RedState extends AccountState{
RedState(double balance,Account account){
this.balance=balance;
this.acc=account;
}
@Override
public void deposit(double amount) {
balance+=amount;
stateCheck();
}
@Override
public void withdraw(double amount) {
System.out.println("该账户不能取款");
}
@Override
public void stateCheck() {
if(balance>=-1000)
if(balance<0)
acc.state=new YellowState(balance,acc);
else
acc.state=new GreenState(balance,acc);
}
}
class GreenState extends AccountState{
GreenState(double balance,Account account){
this.balance=balance;
this.acc=account;
}
@Override
public void deposit(double amount) {
balance+=amount;
stateCheck();
}
@Override
public void withdraw(double amount) {
balance-=amount;
stateCheck();
}
@Override
public void stateCheck() {
if(balance<0)
if(balance>=-1000)
acc.state=new YellowState(balance,acc);
else
acc.state=new RedState(balance,acc);
}
}
class Client7_2{
public static void main(String[] args) {
Account account=new Account("yxh",5.0);
account.deposit(100);
account.withdraw(200);
account.deposit(1000);
account.withdraw(2000);
}
}
yxh开户,初始金额为:5.0
**************************************
yxh存款100.0元
现在余额为105.0
现在账户状态为GreenState
**************************************
yxh取款200.0
现在余额为-95.0
现在账户状态为YellowState
**************************************
yxh存款1000.0元
现在余额为905.0
现在账户状态为GreenState
**************************************
yxh取款2000.0
现在余额为-1095.0
现在账户状态为RedState
**************************************
策略模式
定义一系列算法,封装每个算法,并使它们可以互换。
策略模式可以让算法独立于使用它的客户端。
- Strategy 接口定义了一个算法族,它们都实现了 behavior() 方法。
- Context 是使用到该算法族的类,其中的 doSomething() 方法会调用 behavior(),setStrategy(Strategy) 方法可以动态地改变 strategy 对象,也就是说能动态地改变 Context 所使用的算法。
- 与状态模式的比较
状态模式的类图和策略模式类似,并且都是能够动态改变对象的行为。但是**状态模式是通过状态转移来改变 Context所组合的 State 对象,而策略模式是通过 Context 本身的决策来改变组合的 Strategy 对象。**所谓的状态转移,是指Context 在运行过程中由于一些条件发生改变而使得 State 对象发生改变,注意必须要是在运行过程中。
状态模式主要是用来解决状态转移的问题,当状态发生转移了,那么 Context 对象就会改变它的行为;而策略模式主要是用来封装一组可以互相替代的算法族,并且可以根据需要动态地去替换 Context 使用的算法。
Implementation
设计一个鸭子,它可以动态地改变叫声。这里的算法族是鸭子的叫声行为。
public interface QuackBehavior {
void quack();
}
public class Quack implements QuackBehavior {
@Override
public void quack() {
System.out.println("quack!");
}
}
public class Squeak implements QuackBehavior {
@Override
public void quack() {
System.out.println("squeak!");
}
}
public class Duck {
private QuackBehavior quackBehavior;
public void performQuack() {
if (quackBehavior != null) {
quackBehavior.quack();
}
}
//根据不同的情况设置不同的鸭子叫
public void setQuackBehavior(QuackBehavior quackBehavior) {
this.quackBehavior = quackBehavior;
}
}
public class Client {
public static void main(String[] args) {
Duck duck = new Duck();
duck.setQuackBehavior(new Squeak());
duck.performQuack();
duck.setQuackBehavior(new Quack());
duck.performQuack();
}
}
squeak!
quack!
模板方法
定义算法框架,并将一些步骤的实现延迟到子类。
通过模板方法,子类可以重新定义算法的某些步骤,而不用改变算法的结构。
implementation
冲咖啡和冲茶都有类似的流程,但是某些步骤会有点不一样,要求复用那些相同步骤的代码。
public abstract class CaffeineBeverage {
final void prepareRecipe() {
boilWater();
brew();
pourInCup();
addCondiments();
}
abstract void brew();
abstract void addCondiments();
void boilWater() {
System.out.println("boilWater");
}
void pourInCup() {
System.out.println("pourInCup");
}
}
public class Coffee extends CaffeineBeverage {
@Override
void brew() {
System.out.println("Coffee.brew");
}
@Override
void addCondiments() {
System.out.println("Coffee.addCondiments");
}
}
public class Tea extends CaffeineBeverage {
@Override
void brew() {
System.out.println("Tea.brew");
}
@Override
void addCondiments() {
System.out.println("Tea.addCondiments");
}
}
public class Client {
public static void main(String[] args) {
CaffeineBeverage caffeineBeverage = new Coffee();
caffeineBeverage.prepareRecipe();
System.out.println("-----------");
caffeineBeverage = new Tea();
caffeineBeverage.prepareRecipe();
}
}
boilWater
Coffee.brew
pourInCup
Coffee.addCondiments
-----------
boilWater
Tea.brew
pourInCup
Tea.addCondiments
访问者
表示一个作用于某对象结构中的各元素的操作,它使我们可以在不改变各元素的类的前提下定义作用于这些元素的新操作。举个栗子:医院的处方单,药房人员只关心药品的种类数量,而收费人员只关心药价,所以各对象所关注的数据是不一样的,
- Visitor(抽象访问者)
- ConcreteVisitor(具体访问者)
- Element(抽象元素)
- ConcreteElement(具体元素)
- ObjectStructure(对象结构):可以是组合结构,或者是一个集合
implementation
import java.util.ArrayList;
import java.util.List;
interface Element {
void accept(Visitor visitor);
}
class CustomerGroup {
private List<Customer> customers = new ArrayList<>();
public void accept(Visitor visitor) {
for (Customer customer : customers) {
customer.accept(visitor);
}
}
public void addCustomer(Customer customer) {
customers.add(customer);
}
}
class Customer implements Element {
private String name;
private List<Order> orders = new ArrayList<>();
public Customer(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void addOrder(Order order) {
orders.add(order);
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
for (Order order : orders) {
order.accept(visitor);
}
}
}
class Order implements Element {
private String name;
private List<Item> items = new ArrayList();
public Order(String name) {
this.name = name;
}
public Order(String name, String itemName) {
this.name = name;
this.addItem(new Item(itemName));
}
public String getName() {
return name;
}
public void addItem(Item item) {
items.add(item);
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
for (Item item : items) {
item.accept(visitor);
}
}
}
class Item implements Element {
private String name;
public Item(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
interface Visitor {
void visit(Customer customer);
void visit(Order order);
void visit(Item item);
}
class GeneralReport implements Visitor {
private int customersNo;
private int ordersNo;
private int itemsNo;
@Override
public void visit(Customer customer) {
System.out.println(customer.getName());
customersNo++;
}
@Override
public void visit(Order order) {
System.out.println(order.getName());
ordersNo++;
}
@Override
public void visit(Item item) {
System.out.println(item.getName());
itemsNo++;
}
void displayResults() {
System.out.println("Number of customers: " + customersNo);
System.out.println("Number of orders: " + ordersNo);
System.out.println("Number of items: " + itemsNo);
}
}
class Clientv {
public static void main(String[] args) {
Customer customer1 = new Customer("customer1");
customer1.addOrder(new Order("order1", "item1"));
customer1.addOrder(new Order("order2", "item1"));
customer1.addOrder(new Order("order3", "item1"));
Order order = new Order("order_a");
order.addItem(new Item("item_a1"));
order.addItem(new Item("item_a2"));
order.addItem(new Item("item_a3"));
Customer customer2 = new Customer("customer2");
customer2.addOrder(order);
CustomerGroup customers = new CustomerGroup();
customers.addCustomer(customer1);
customers.addCustomer(customer2);
GeneralReport visitor = new GeneralReport();
customers.accept(visitor);
visitor.displayResults();
}
}
customer1
order1
item1
order2
item1
order3
item1
customer2
order_a
item_a1
item_a2
item_a3
Number of customers: 2
Number of orders: 4
Number of items: 6
解析:文中的element就是item order customer,customer包含 order ,order包含item ,visitor传入不同的element 遍历就会不同,比如传入的是customer,就会先获取customer的name,再遍历里面的order
结构型
适配器
把一个类接口转换成另一个用户需要的接口。
- Target:目标抽象类定义客户要用的特定领域的接口
- Adpater:适配器类作为一个转换器,对Adaptee和Target进行适配
- Adaptee:适配者即被适配的角色,adpatee可以作为成员变量被adapter拥有,也可以作为父类被adapter继承,根据“合成复用原则”,在系统中尽量使用关联关系来替代继承关系。
implementation
鸭子(Duck)和火鸡(Turkey)拥有不同的叫声,Duck 的叫声调用 quack() 方法,而 Turkey 调用 gobble() 方法。
要求将 Turkey 的 gobble() 方法适配成 Duck 的 quack() 方法,从而让火鸡冒充鸭子!
//Target类
public interface Duck {
void quack();
}
//Adaptee类
public interface Turkey {
void gobble();
}
public class WildTurkey implements Turkey {
@Override
public void gobble() {
System.out.println("gobble!");
}
}
//adpter
public class TurkeyAdapter implements Duck {
Turkey turkey;
public TurkeyAdapter(Turkey turkey) {
this.turkey = turkey;
}
//适配方法
@Override
public void quack() {
turkey.gobble();
}
}
public class Client {
public static void main(String[] args) {
Turkey turkey = new WildTurkey();
Duck duck = new TurkeyAdapter(turkey);
duck.quack();
}
}
桥接
将抽象和实现分离开来,使它们可以独立变化(把抽象部分和实现部分桥接)
-
Abstraction:抽象类
-
RefinedAbstraction:扩充抽象类
-
Implementor:实现类接口
-
ConcreteImplementor:具体实现类
-
注意Abstraction中的箭头指向implementor的意思是:实现了Abstraction接口的接口实现类作为implor的成员变量
-
一般固有属性作为抽象部分,附加属性或者外在属性作为实现部分,如毛笔与颜色的关系:
优点:
- 分离抽象接口及其实现部分
- 可以取代多层继承方案,极大地减少了子类的个数
- 提高了系统的可扩展性,在两个变化维度中任意扩展一个维度,不需要修改原有系统,符合开闭原则
Implementation
RemoteControl 表示遥控器,指代 Abstraction。
TV 表示电视,指代 Implementor。
桥接模式将遥控器和电视分离开来,从而可以独立改变遥控器或者电视的实现。
不同品牌的遥控器和不同品牌的TV机,(有点像比如不同型号的毛笔和不同颜色的墨水)
public abstract class TV {
public abstract void on();
public abstract void off();
public abstract void tuneChannel();
}
public class Sony extends TV {
@Override
public void on() {
System.out.println("Sony.on()");
}
@Override
public void off() {
System.out.println("Sony.off()");
}
@Override
public void tuneChannel() {
System.out.println("Sony.tuneChannel()");
}
}
public class RCA extends TV {
@Override
public void on() {
System.out.println("RCA.on()");
}
@Override
public void off() {
System.out.println("RCA.off()");
}
@Override
public void tuneChannel() {
System.out.println("RCA.tuneChannel()");
}
}
public abstract class RemoteControl {
protected TV tv;
public RemoteControl(TV tv) {
this.tv = tv;
}
public abstract void on();
public abstract void off();
public abstract void tuneChannel();
}
public class ConcreteRemoteControl1 extends RemoteControl {
public ConcreteRemoteControl1(TV tv) {
super(tv);
}
@Override
public void on() {
System.out.println("ConcreteRemoteControl1.on()");
tv.on();
}
@Override
public void off() {
System.out.println("ConcreteRemoteControl1.off()");
tv.off();
}
@Override
public void tuneChannel() {
System.out.println("ConcreteRemoteControl1.tuneChannel()");
tv.tuneChannel();
}
}
public class ConcreteRemoteControl2 extends RemoteControl {
public ConcreteRemoteControl2(TV tv) {
super(tv);
}
@Override
public void on() {
System.out.println("ConcreteRemoteControl2.on()");
tv.on();
}
@Override
public void off() {
System.out.println("ConcreteRemoteControl2.off()");
tv.off();
}
@Override
public void tuneChannel() {
System.out.println("ConcreteRemoteControl2.tuneChannel()");
tv.tuneChannel();
}
}
public class Client {
public static void main(String[] args) {
RemoteControl remoteControl1 = new ConcreteRemoteControl1(new RCA());
remoteControl1.on();
remoteControl1.off();
remoteControl1.tuneChannel();
RemoteControl remoteControl2 = new ConcreteRemoteControl2(new Sony());
remoteControl2.on();
remoteControl2.off();
remoteControl2.tuneChannel();
}
}
组合模式
将对象组合成树形结构来表示“整体/部分”层次关系,允许用户以相同的方式处理单独对象和组合对象。(如文件夹的表示)
- 举个栗子就明白了:Component是文件或者文件夹,而Leaf是文件,Composite是文件夹,文件夹里可能还有文件或者文件夹
- 组件(Component)类是组合类(Composite)和叶子类(Leaf)的父类
- 组合对象拥有一个或者多个组件对象,因此组合对象的操作可以委托给组件对象去处理,而组件对象可以是另一个组合对象或者叶子对象。
Implementation
public abstract class Component {
protected String name;
public Component(String name) {
this.name = name;
}
public void print() {
print(0);
}
abstract void print(int level);
abstract public void add(Component component);
abstract public void remove(Component component);
}
public class Composite extends Component {
private List<Component> child;
public Composite(String name) {
super(name);
child = new ArrayList<>();
}
@Override
void print(int level) {
for (int i = 0; i < level; i++) {
System.out.print("
--
");
}
System.out.println("Composite:" + name);
for (Component component : child) {
component.print(level + 1);
}
}
@Override
public void add(Component component) {
child.add(component);
}
@Override
public void remove(Component component) {
child.remove(component);
}
}
public class Leaf extends Component {
public Leaf(String name) {
super(name);
}
@Override
void print(int level) {
for (int i = 0; i < level; i++) {
System.out.print("
--
");
}
System.out.println("left:" + name);
}
@Override
public void add(Component component) {
// 牺牲透明性换取单一职责原则,这样就不用考虑是叶子节点还是组合节点
throw new UnsupportedOperationException();
}
@Override
public void remove(Component component) {
throw new UnsupportedOperationException();
}
}
public class Client {
public static void main(String[] args) {
Composite root = new Composite("root");
Component node1 = new Leaf("1");
Component node2 = new Composite("2");
Component node3 = new Leaf("3");
root.add(node1);
root.add(node2);
root.add(node3);
Component node21 = new Leaf("21");
Component node22 = new Composite("22");
node2.add(node21);
node2.add(node22);
Component node221 = new Leaf("221");
node22.add(node221);
root.print();
}
}
Composite:root
--left:1
--Composite:2
----left:21
----Composite:22
------left:221
--left:3
装饰
为对象动态添加功能。
- Component: 抽象构件类
- ConcreteComponent: 具体构件类
- Decorator: 抽象装饰类
- ConcreteDecorator: 具体装饰类
- 以利用装饰者decorator动态的往component添加新功能,decorator是实现了component接口(为了与component的子类对象,也就是它所修饰的对象有同样的方法),同时把component作为成员变量,是为了操作修饰的对象
implementation
设计不同种类的饮料,饮料可以添加配料,比如可以添加牛奶,并且支持动态添加新配料。每增加一种配料,该饮料的价格就会增加,要求计算一种饮料的价格。
下图表示在 DarkRoast 饮料上新增新添加 Mocha 配料,之后又添加了 Whip 配料。DarkRoast 被 Mocha 包裹,Mocha 又被 Whip 包裹。它们都继承自相同父类,都有 cost() 方法,外层类的 cost() 方法调用了内层类的 cost() 方法。
//beverage 饮料
public interface Beverage {
double cost();
}
//darkroast 深烘焙 一种饮料口味
public class DarkRoast implements Beverage {
@Override
public double cost() {
return 1;
}
}
//House Blend 也是一种饮料口味
public class HouseBlend implements Beverage {
@Override
public double cost() {
return 1;
}
}
//装饰者
public abstract class CondimentDecorator implements Beverage {
protected Beverage beverage;
}
//牛奶
public class Milk extends CondimentDecorator {
public Milk(Beverage beverage) {
this.beverage = beverage;
}
@Override
public double cost() {
return 1 + beverage.cost();
}
}
//摩卡
public class Mocha extends CondimentDecorator {
public Mocha(Beverage beverage) {
this.beverage = beverage;
}
@Override
public double cost() {
return 1 + beverage.cost();
}
}
public class Client {
public static void main(String[] args) {
//造一杯咖啡
Beverage beverage = new HouseBlend();
//咖啡里加摩卡 ,这里体现装饰
beverage = new Mocha(beverage);
//咖啡里加牛奶,体现装饰
beverage = new Milk(beverage);
System.out.println(beverage.cost());
}
}
3.0
设计原则
类应该对扩展开放,对修改关闭:也就是添加新功能时不需要修改代码。饮料可以动态添加新的配料,而不需要去修改饮料的代码。(也就是说,可以利用装饰者decorator动态的往component添加新功能,decorator是实现了component接口(为了与component的子类对象,也就是它所修饰的对象有同样的方法),同时把component作为成员变量,是为了操作修饰的对象)
不可能把所有的类设计成都满足这一原则,应当把该原则应用于最有可能发生改变的地方。
外观(facade)
提供了一个统一的接口,用来访问子系统中的一群接口,从而让子系统更容易使用。
implementation
观看电影需要操作很多电器,使用外观模式实现一键看电影功能。
public interface Investor {
public void response(Stock stock);
}
public class SubSystemA {
public void turnOnTV() {
System.out.println("turnOnTV()");
}
}
public class SubSystemB {
public void setCD(String cd) {
System.out.println("setCD( " + cd + " )");
}
}
public class SubSystemC {
public void startWatching() {
System.out.println("startWatching()");
}
}
public class Facade {
private SubSystemA subSystemA = new SubSystemA();
private SubSystemB subSystemB = new SubSystemB();
private SubSystemC subSystemC = new SubSystemC();
public void watchMovie() {
subSystemA.turnOnTV();
subSystemB.setCD("a movie");
subSystemC.startWatching();
}
}
public class Client {
public static void main(String[] args) {
Facade facade = new Facade();
facade.watchMovie();
}
}
implementation2
(其实就是三个文件操作聚合在同一个类,这个模式的中心思想也是如此)
某系统需要提供一个文件加密模块,加密流程包括三个操作,分别是读取源文件、加密、保存加密之后的文件。读取文件和保存文件使用流来实现,这三个操作相对独立,其业务代码封装在三个不同的类中。现在需要提供一个统一的加密外观类,用户可以直接使用该加密外观类完成文件的读取、加密和保存三个操作,而不需要与每一个类进行交互,使用外观模式设计该加密模块。请根据类图编程实现该系统,并写出相应Java代码。
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
* @author:yxh
*外观模式实例
*/
//加密外观类:外观类
class EncryptFacade {
/************************/
private FileReader reader;
private CipherMachine cipher;
private FileWriter writer;
EncryptFacade() {
reader = new FileReader();
cipher = new CipherMachine();
writer = new FileWriter();
}
public void fileEncrypt(String fileNameSrc, String fileNameDes) {
//从文件中读取的字符
String fileString = reader.read(fileNameSrc);
//加密后的字符
String encryptedString = cipher.encrypt(fileString);
//保存
writer.write(encryptedString, fileNameDes);
}
/************************/
}
//文件读取类:子系统类
class FileReader {
public String read(String fileNameSrc) {
System.out.println("读取文件,获取明文。");
StringBuffer sb = new StringBuffer();
try {
FileInputStream inFS = new FileInputStream(fileNameSrc);
int data;
while ((data = inFS.read()) != -1) {
sb = sb.append((char) data);
}
inFS.close();
} catch (FileNotFoundException e) {
System.out.println("文件不存在!");
} catch (IOException e) {
System.out.println("文件操作错误!");
}
return sb.toString();
}
}
//数据加密类:子系统类
class CipherMachine {
public String encrypt(String plainText) {
System.out.println("数据加密,将明文转换为密文。");
String es = "";
for (int i = 0; i < plainText.length(); i++) {
String c = String.valueOf(plainText.charAt(i) % 7);
es += c;
}
return es;
}
}
//文件保存类:子系统类
class FileWriter {
public void write(String encryptStr, String fileNameDes) {
System.out.println("保存密文,写入文件。");
try {
FileOutputStream outFS = new FileOutputStream(fileNameDes);
outFS.write(encryptStr.getBytes());
outFS.close();
} catch (FileNotFoundException e) {
System.out.println("文件不存在!");
} catch (IOException e) {
System.out.println("文件操作错误!");
}
}
}
//客户端测试类
class Client3_1 {
public static void main(String args[]) {
//********************************//
EncryptFacade ef = new EncryptFacade();
ef.fileEncrypt("src.txt", "des.txt");
//********************************//
}
}
设计原则
最少知识原则:只和你的密友谈话。也就是说客户对象所需要交互的对象应当尽可能少。
享元模式
利用共享的方式来支持大量细粒度的对象,这些对象一部分内部状态是相同的。
- JDK中的应用
Java 利用缓存来加速大量小对象的访问时间。
java.lang.Integer#valueOf(int)
java.lang.Boolean#valueOf(boolean)
java.lang.Byte#valueOf(byte)
java.lang.Character#valueOf(char)
- Flyweight:享元对象
- IntrinsicState:内部状态,享元对象共享内部状态
- ExtrinsicState:外部状态,每个享元对象的外部状态不同
implementation
public interface Flyweight {
void doOperation(String extrinsicState);
}
public class ConcreteFlyweight implements Flyweight {
private String intrinsicState;
public ConcreteFlyweight(String intrinsicState) {
this.intrinsicState = intrinsicState;
}
@Override
public void doOperation(String extrinsicState) {
System.out.println("Object address: " + System.identityHashCode(this));
System.out.println("IntrinsicState: " + intrinsicState);
System.out.println("ExtrinsicState: " + extrinsicState);
}
}
public class FlyweightFactory {
private HashMap<String, Flyweight> flyweights = new HashMap<>();
Flyweight getFlyweight(String intrinsicState) {
if (!flyweights.containsKey(intrinsicState)) {
Flyweight flyweight = new ConcreteFlyweight(intrinsicState);
flyweights.put(intrinsicState, flyweight);
}
return flyweights.get(intrinsicState);
}
}
public class Client {
public static void main(String[] args) {
FlyweightFactory factory = new FlyweightFactory();
Flyweight flyweight1 = factory.getFlyweight("aa");
Flyweight flyweight2 = factory.getFlyweight("aa");
flyweight1.doOperation("x");
flyweight2.doOperation("y");
}
}
从输出结果可以看到,flyweight1和flyweight2是同一个变量 也就是共享的变量,而doOperation方法传入的是不同的参数。
Object address: 1163157884
IntrinsicState: aa
ExtrinsicState: x
Object address: 1163157884
IntrinsicState: aa
ExtrinsicState: y
implementation2
某软件公司设计一个围棋软件,在系统中只存在一个白棋对象和一个黑棋对象,但是他们可以在棋盘的不同位置显示多次。请使用享元模式设计该软件,并写出相应Java代码。要求使用简单工厂模式和单例模式实现享元工厂类的设计。
/**
* 享元模式实例
* @author:yxh
*/
import java.util.Hashtable;
/**
* 享元工厂
* */
class IgoChessmanFactory{
private static Hashtable ht;
private static IgoChessmanFactory instance;
public IgoChessmanFactory() {
ht=new Hashtable();
ht.put("b",new BlackIgoChessman());
ht.put("w",new WhiteIgoChessman());
}
public static IgoChessmanFactory getInstance(){
if(instance==null){
synchronized (IgoChessmanFactory.class){
if(instance==null)
instance=new IgoChessmanFactory();
}
}
return instance;
}
public IgoChessman getIgoChessman(String color){
return (IgoChessman) ht.get(color);
}
}
/**
* 黑棋子
*/
class BlackIgoChessman extends IgoChessman{
@Override
String getColor() {
return "黑色";
}
@Override
void display(Coordinates coordinates) {
System.out.println("棋子颜色:"+this.getColor()+",棋子位置:"+coordinates.getX()+","+coordinates.getY());
}
}
/**
* 白棋子
*/
class WhiteIgoChessman extends IgoChessman{
@Override
String getColor() {
return "白色";
}
@Override
void display(Coordinates coordinates) {
System.out.println("棋子颜色:"+this.getColor()+",棋子位置:"+coordinates.getX()+","+coordinates.getY());
}
}
/**
* 棋子抽象类
*/
abstract class IgoChessman{
abstract String getColor();
abstract void display(Coordinates coordinates);
}
/**
* 坐标类
*/
class Coordinates{
int x;
int y;
public Coordinates(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
/**
* 测试类
*/
class Client{
public static void main(String[] args) {
IgoChessman black1,black2,black3,white1,white2;
IgoChessmanFactory factory;
factory=IgoChessmanFactory.getInstance();
black1=factory.getIgoChessman("b");
black2=factory.getIgoChessman("b");
black3=factory.getIgoChessman("b");
System.out.println("判断两颗黑棋是否相同"+(black1==black2));
white1=factory.getIgoChessman("w");
white2=factory.getIgoChessman("w");
System.out.println("判断两颗白棋是否相同"+(white1==white2));
black1.display(new Coordinates(1,2));
black2.display(new Coordinates(3,4));
black3.display(new Coordinates(5,2));
white1.display(new Coordinates(1,2));
white2.display(new Coordinates(8,7));
}
}
判断两颗黑棋是否相同true
判断两颗白棋是否相同true
棋子颜色:黑色,棋子位置:1,2
棋子颜色:黑色,棋子位置:3,4
棋子颜色:黑色,棋子位置:5,2
棋子颜色:白色,棋子位置:1,2
棋子颜色:白色,棋子位置:8,7
代理模式
-
Subject:抽象主题类
-
Proxy:代理类
-
RealSubject:真实主题类
-
客户端可以通过代理类间接调用真实主题类的方法,同时代理类继承Subject并且RealSubject作为Proxy的成员变量。
基于JDK的动态代理实现步:
第一步,创建一个接口(abstractSubject)
第二步,创建实现abstractSubject接口的类(realSubject)
第三步,创建实现InvocationHandler接口的类,在invoke方法中实现业务代理
第四步,利用Proxy类提供的静态工厂方法创建代理类(Proxy)。
第五步,通过代理类调用接口定义的方法。
代理主要有以下四类
- 远程代理(Remote Proxy):控制对远程对象(不同地址空间)的访问,它负责将请求及其参数进行编码,并向不同地址空间中的对象发送已经编码的请求。
- 虚拟代理(Virtual Proxy):根据需要创建开销很大的对象,它可以缓存实体的附加信息,以便延迟对它的访问,例如在网站加载一个很大图时,不能马上完成,可以用虚拟代理缓存图片的大小信息,然后生成一张临时图片代替原始图片。
- 保护代理(Protection Proxy):按权限控制对象的访问,它负责检查调用者是否具有实现一个请求所必须的访问权限。
- 智能代理(Smart Reference):取代了简单的指针,它在访问对象时执行一些附加操作:记录对象的引用次数;当第一次引用一个对象时,将它装入内存;在访问一个实际对象前,检查是否已经锁定了它,以确保其它
对象不能改变它。
implementation
import java.util.*;
//抽象日志记录类:抽象主题
interface AbstractLog
{
public void method();
}
//业务类:真实主题
class BusinessClass implements AbstractLog
{
public void method()
{
System.out.println("真实业务方法!");
}
}
//日志记录代理类:代理主题
class LoggerProxy implements AbstractLog
{
private BusinessClass business;
public LoggerProxy()
{
business = new BusinessClass();
}
public void method()
{
Calendar calendar = new GregorianCalendar();
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH) + 1;
int day = calendar.get(Calendar.DAY_OF_MONTH);
int hour = calendar.get(Calendar.HOUR) + 12;
int minute = calendar.get(Calendar.MINUTE);
int second = calendar.get(Calendar.SECOND);
String dateTime = year + "-" + month + "-" + day + " " + hour + ":" + minute + ":" + second + "!";
System.out.println("方法method()被调用,调用时间为" + dateTime);
try{
business.method();
System.out.println("方法method()调用成功!");
}
catch(Exception e)
{
System.out.println("方法method()调用失败!");
}
}
}
//客户端测试类
public class ProxyPattern
{
public static void main(String args[])
{
AbstractLog al;
al = new LoggerProxy();
al.method();
}
}
方法method()被调用,调用时间为2020-7-2 16:1:4!
真实业务方法!
方法method()调用成功!
设计原则
设计原则名称 | 设计原则简介 |
---|---|
单一职责原则(Single Responsibility Principle, SRP) | 类的职责要单一,不能将太多的职责放在一个类中 |
开闭原则(Open-Closed Principle, OCP) | 软件实体对扩展是开放的,但对修改是关闭的,即在不修改一个软件实体的基础上去扩展其功能 |
里氏代换原则(Liskov Substitution Principle, LSP) | 在软件系统中,一个可以接受基类对象的地方必然可以接受一个子类对象 |
依赖倒转原则(Dependency Inversion Principle, DIP) | 要针对抽象层编程,而不要针对具体类编程 |
接口隔离原则(Interface Segregation Principle, ISP) | 使用多个专门的接口来取代一个统一的接口 |
合成复用原则(Composite Reuse Principle, CRP) | 在系统中应该尽量多使用组合和聚合关联关系,尽量少使用甚至不使用继承关系 |
迪米特法则(Law of Demeter, LoD) | 一个软件实体对其他实体的引用越少越好,或者说如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用,而是通过引入一个第三者发生间接交互。 |
色:黑色,棋子位置:3,4
棋子颜色:黑色,棋子位置:5,2
棋子颜色:白色,棋子位置:1,2
棋子颜色:白色,棋子位置:8,7
## 代理模式
<img src="https://gitee.com/yxh99/imagebed/raw/master/img/image-20200702153540971.png" alt="image-20200702153540971" style="zoom:200%;" />
- Subject:抽象主题类
- Proxy:代理类
- RealSubject:真实主题类
- 客户端可以通过代理类间接调用真实主题类的方法,同时代理类继承Subject并且RealSubject作为Proxy的成员变量。
### 基于JDK的动态代理实现步:
第一步,创建一个接口(abstractSubject)
第二步,创建实现abstractSubject接口的类(realSubject)
第三步,创建实现InvocationHandler接口的类,在invoke方法中实现业务代理
第四步,利用Proxy类提供的静态工厂方法创建代理类(Proxy)。
第五步,通过代理类调用接口定义的方法。
### 代理主要有以下四类
- 远程代理(Remote Proxy):**控制对远程对象(不同地址空间)的访问**,它负责将请求及其参数进行编码,并向不同地址空间中的对象发送已经编码的请求。
- 虚拟代理(Virtual Proxy):**根据需要创建开销很大的对象,它可以缓存实体的附加信息,以便延迟对它的访问**,例如在网站加载一个很大图时,不能马上完成,可以用虚拟代理缓存图片的大小信息,然后生成一张临时图片代替原始图片。
- 保护代理(Protection Proxy):**按权限控制对象的访问,它负责检查调用者是否具有实现一个请求所必须的访问权限**。
- 智能代理(Smart Reference):**取代了简单的指针,它在访问对象时执行一些附加操作**:记录对象的引用次数;当第一次引用一个对象时,将它装入内存;在访问一个实际对象前,检查是否已经锁定了它,以确保其它
对象不能改变它。
### implementation
```java
import java.util.*;
//抽象日志记录类:抽象主题
interface AbstractLog
{
public void method();
}
//业务类:真实主题
class BusinessClass implements AbstractLog
{
public void method()
{
System.out.println("真实业务方法!");
}
}
//日志记录代理类:代理主题
class LoggerProxy implements AbstractLog
{
private BusinessClass business;
public LoggerProxy()
{
business = new BusinessClass();
}
public void method()
{
Calendar calendar = new GregorianCalendar();
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH) + 1;
int day = calendar.get(Calendar.DAY_OF_MONTH);
int hour = calendar.get(Calendar.HOUR) + 12;
int minute = calendar.get(Calendar.MINUTE);
int second = calendar.get(Calendar.SECOND);
String dateTime = year + "-" + month + "-" + day + " " + hour + ":" + minute + ":" + second + "!";
System.out.println("方法method()被调用,调用时间为" + dateTime);
try{
business.method();
System.out.println("方法method()调用成功!");
}
catch(Exception e)
{
System.out.println("方法method()调用失败!");
}
}
}
//客户端测试类
public class ProxyPattern
{
public static void main(String args[])
{
AbstractLog al;
al = new LoggerProxy();
al.method();
}
}
方法method()被调用,调用时间为2020-7-2 16:1:4!
真实业务方法!
方法method()调用成功!
设计原则
设计原则名称 | 设计原则简介 |
---|---|
单一职责原则(Single Responsibility Principle, SRP) | 类的职责要单一,不能将太多的职责放在一个类中 |
开闭原则(Open-Closed Principle, OCP) | 软件实体对扩展是开放的,但对修改是关闭的,即在不修改一个软件实体的基础上去扩展其功能 |
里氏代换原则(Liskov Substitution Principle, LSP) | 在软件系统中,一个可以接受基类对象的地方必然可以接受一个子类对象 |
依赖倒转原则(Dependency Inversion Principle, DIP) | 要针对抽象层编程,而不要针对具体类编程 |
接口隔离原则(Interface Segregation Principle, ISP) | 使用多个专门的接口来取代一个统一的接口 |
合成复用原则(Composite Reuse Principle, CRP) | 在系统中应该尽量多使用组合和聚合关联关系,尽量少使用甚至不使用继承关系 |
迪米特法则(Law of Demeter, LoD) | 一个软件实体对其他实体的引用越少越好,或者说如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用,而是通过引入一个第三者发生间接交互。 |
迪米特法则补充:迪米特法则就是指一个软件实体应当尽可能少的与其他实体发生相互作用。这样,当一个模块修改时,就会尽量少的影响其他的模块,扩展会相对容易,这是对软件实体之间通信的限制,它要求限制软件实体之间通信的宽度和深度。