设计模式的七大原则
单一原则
对类来说,即一个类应该只负责一个职责。如果类A负责两个不同的职责:职责1、职责2,当职责1需求变更而改变类A时,可能造成职责2执行错误,所以需要将类A的粒度分解为A1、A2。
注意事项
- 单一原则的目的是为了降低类的复杂度,一个类只负责一项职责;
- 提高类的可读性、可维护性,降低变更引起的风险;
- 通常情况下我们都应当遵守单一原则,只有逻辑足够简单才可以在代码级违反单一职责原则,只有类中的方法足够少,才可以在方法级别保持单一原则。
接口隔离原则
客户端不应该依赖他不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上。
对上面解释的理解,可以理解为:
当接口I
中有非常多的接口方法的时候,有类A、B、C、D都需要用到其一部分的方法。但是实现这个接口后又不得不实现很多自己并用不到的方法。由此,接口I
就不是一个最小粒度的接口,应当按照实际进行接口的拆分,让A、B、C、D四个类都只实现自己必须的方法,满足接口隔离原则。
依赖倒转原则
- 高层模块不应该依赖低层模块,二者都应该依赖其抽象;
- 抽象不应该依赖细节,细节应该依赖抽象;
- 依赖倒转原则的中心思想是面向接口编程;
- 依赖倒转原则的设计理念:相对于细节的多变性,抽象的东西要稳定得多,以抽象为基础搭建的架构比以细节为基础的架构要稳定得多。(在java中,抽象一般指的是接口和抽象类,细节指的是实现类。)
- 使用接口和抽象的目的就是制定好规范,而不涉及任何具体的操作,把展示细节的操作交给它们的实现类去完成。
程序案例理解
package com.xsh.design_mode.dependency_inversion;
public class Test {
public static void main(String[] args) {
Person person = new Person();
person.receivedEmail(new Email());
person.receivedWechat(new Wechat());
}
}
class Email {
public void getInfo(){
System.out.println("发送邮件");
}
}
class Wechat {
public void getInfo() {
System.out.println("发送微信");
}
}
class Person{
public void receivedEmail(Email email){
email.getInfo();
}
public void receivedWechat(Wechat wechat){
wechat.getInfo();
}
}
问题解析:
如上的实例可以看出,在Person类中完成发送邮件和发送微信这样相同的功能的时候,由于Person类依赖了具体的实现细节,所以导致再进行拓展的时候十分困难,造成代码的臃肿。此时,如果进行依赖的倒置,Person类依赖于更抽象的内容就能够更加优雅的拓展。
package com.xsh.design_mode.dependency_inversion;
public class Test {
public static void main(String[] args) {
Person person = new Person();
person.received(new Email());
person.received(new Wechat());
}
}
interface Send{
void getInfo();
}
class Email implements Send{
@Override
public void getInfo(){
System.out.println("发送邮件");
}
}
class Wechat implements Send{
@Override
public void getInfo() {
System.out.println("发送微信");
}
}
/**
* 依赖面向接口之后,接口实现的拓展Person并不需要进行任何的改动
*/
class Person{
public void received(Send send){
send.getInfo();
}
}
里氏替换原则
在类的继承关系中,子类应当尽量不重写父类的方法。
类的继承本身就是一种耦合的行为,如果在继承的情况下还重写了父类的方法就会导致这个方法的继承耦合没有任何意义。
(你继承我就是为了用我的方法,你还要重写我的方法,那就不如不继承!)
开闭原则
一个软件实体如类、模块和函数应当对拓展开放,对修改关闭。用抽象构建框架,用实现拓展细节。
当软件需要变化拓展时,尽量通过拓展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。
迪米特法则
迪米特法则又叫最少知道原则,即一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类不管多么复杂,都尽量将其逻辑封装在类的内部,对外只提供public方法,不对外泄露任何信息。
合成复用原则
能够使用合成/聚合的方式就尽量不要使用继承。
(当类A希望用到类B的方法的时候,尽量不要去直接继承它,可以使用将类B变成方法入参或者成员变量的方式来映它,这样可以避免类继承造成的耦合性。)
单例模式
所谓单例模式,就是采取一定的方法保证在整个软件系统中对某个类只能存在一个对象实例,并且该类只提供一个获取其对象实例的方法。
饿汉式实现
package com.xsh.design_mode.singleton_pattern;
public class Singleton {
private final static Singleton instance = new Singleton();
private Singleton() {};
public static Singleton getInstance() {
return instance;
}
}
优缺点分析:
- 优点:实现简单,在类加载的时候就利用classLoader机制来避免了线程同步问题;
- 缺点:不是一种懒加载的方式,在类加载的时候就会创建对象,不管程序中是否使用该对象都会创建,可能造成内存的浪费。
懒汉式实现
package com.xsh.design_mode.singleton_pattern;
public class Singleton {
private static Singleton instance;
private Singleton() {};
/**
* 实现懒加载对象实例,并使用synchronized保证线程安全
*/
public static synchronized Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
优缺点分析:
- 优点:实现了单例对象的懒加载,确保了单例对象的线程安全;
- 缺点:同步关键字使得其效率降低。
双重检查实现(懒加载)
package com.xsh.design_mode.singleton_pattern;
public class Singleton {
private static volatile Singleton instance;
private Singleton() {};
/**
* 双重检查实现相对于同步方法的实现锁的粒度更小
* 而且在流程上只对第一次实例化对象的过程加锁,更合理
*/
public static Singleton getInstance() {
if(instance == null) {
synchronized(Singleton.class) {
if(instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
静态内部类实现
package com.xsh.design_mode.singleton_pattern;
public class Singleton {
private Singleton() {};
private static class SingletonInstance {
private static final Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return SingletonInstance.instance;
}
}
静态内部类实现单例的原理:
- 私有化的静态内部类无法被外部访问;
- 在调用getInstance函数的时候才会启动静态内部类的装载,是懒加载的形式;
- 类的加载时利用了classLoader机制,是线程安全的。
枚举实现
package com.xsh.design_mode.singleton_pattern;
public enum Singleton {
INSTANCE;
public void ok() {
System.out.println("ok~");
}
}
工厂模式
简单工厂模式
简单工厂模式是属于创建型模式,是工厂模式的一种。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例,简单工厂模式是工厂家族中最简单实用的模式。
简单工厂模式实际上就是定义一个创建对象的类,由这个类来封装实例化对象的行为。
/**
* 抽象类,定义相同规则的实例特征
*/
public abstract class Animal {
private String name;
public void setName(String name) {
this.name = name;
}
public void call() {
System.out.println(this.name + "叫了~");
}
}
/**
* 继承抽象,是一个具体的实例
*/
public class Cat extends Animal{
}
/**
* 继承抽象,是一个具体的实例
*/
public class Dog extends Animal{
}
/**
* 取代主动new的形式,来自动创建不同的实例
*/
public class SimpleFactory {
public static Animal getInstance(String animalType) {
Animal animal = null;
if("cat".equals(animalType)) {
animal = new Cat();
animal.setName("cat");
} else if("dog".equals(animalType)) {
animal = new Dog();
animal.setName("dog");
}
return animal;
}
}
public class Test {
public static void main(String[] args) {
SimpleFactory.getInstance("cat").call();
SimpleFactory.getInstance("dog").call();
}
}
抽象工厂模式
在简单工厂模式中,我们通过工厂类来实现实例化cat和dog的具体实例。此时,如果对象的层级更高,例如我们需要实例化的是哈士奇或者布偶猫这样层级更低更具体的实例,我们就必须在工厂方法的getInstance函数中建立更多的分支,这样也是比较麻烦且逻辑混乱的。
此时,可以调整实现方式为抽象工厂模式,根据实例化对象的层级,将顶层的工厂方法抽象,由具体的类别工厂来实现,实例如下:
/**
* 抽象类,定义相同规则的实例特征
*/
public abstract class Animal {
private String name;
public void setName(String name) {
this.name = name;
}
public void call() {
System.out.println(this.name + "叫了~");
}
}
/**
* 更细粒度且具有相似特征的实例
*/
public class BuouCat extends Animal{
}
/**
* 更细粒度且具有相似特征的实例
*/
public class LihuaCat extends Animal{
}
/**
* 抽象方法工厂
*/
public abstract class FactoryMethod {
public Animal getInstance(String animalType) {
return createInstance(animalType);
}
abstract Animal createInstance(String animalType);
}
/**
* 具有特性的工厂方法实现
*/
public class DogFactory extends FactoryMethod{
@Override
Animal createInstance(String animalType) {
Animal animal = null;
if("hashiqi".equals(animalType)) {
animal = new HashiqiDog();
animal.setName("哈士奇");
}else if("qitian".equals(animalType)) {
animal = new QiutianDog();
animal.setName("秋田犬");
}
return animal;
}
}
public class Test {
public static void main(String[] args) {
FactoryMethod factory = new CatFactory();
factory.getInstance("lihua").call();
FactoryMethod dogFactory = new DogFactory();
dogFactory.getInstance("hashiqi").call();
}
}
原型模式
从案例问题来分析:
当我们有一个标准的业务对象,需要依据这个对象来创建更多个跟他一样的内容的实例对象。
解决这个问题最简单的方式就是循环创建对象,并从标准的业务对象中取出他的属性来赋值到新的对象属性中。然,这样的操作是非常笨拙的,创建新的对象的时候总是要从标准业务对象中获取数据,而不是动态的获取对象运行时的状态。
优化思路:
Java中Object类是所有类的基类,Object类提供了一个clone函数,该方法可以将一个java对象复制一份。但是实现clone的java类必须要实现一个Cloneable的接口,该接口表示该类能够复制并且具有复制的能力。
原型模式是指用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象。原型模式是一种建造型设计模式,允许一个对象再创建另外一个可定制的对象,而无需知道创建对象的细节。工作原理是将一个原型对象传递给要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实现创建,即对象.clone()。
package com.xsh.design_mode.prototype_pattern;
public class Person implements Cloneable {
private String name;
private Integer age;
public String getName() {
return name;
}
public Integer getAge() {
return age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
/**
* 重写clone方法完成对象的复制
* @return
* @throws CloneNotSupportedException
*/
@Override
protected Person clone() throws CloneNotSupportedException {
return (Person) super.clone();
}
}
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Person person = new Person("张三", 16);
Person clone1 = person.clone();
Person clone2 = person.clone();
Person clone3 = person.clone();
System.out.println(person);
System.out.println(clone1);
System.out.println(clone2);
System.out.println(clone3);
}
}
使用clone()可以动态的完成对象的复制,原型对象数据和属性的变化会自动影响到克隆产生的实例。
关于浅拷贝和深拷贝
浅拷贝
- 对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值的传递,也就是将该属性值复制一份给新的对象;
- 对于数据类型是引用类型的成员变量,比如成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只将该成员变量的内存地址复制一份给新的对象,实际上两个对象的成员变量都指向一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值。
(在上述案例中通过clone函数实现的原型模式就是浅拷贝。)
关于基本数据类型包装类型及String类进行浅克隆的处理
浅克隆只对于基本数据类型的成员变量才会采用复制值的形式,对于引用数据类型采用的是复制引用。但是面对基本数据类型的包装类型和String类的时候,实际上也是进行的复制值,对于这两种情况的成员变量复制之后其实也是隔离的,互不干扰。
原因是因为,这些类型的成员变量都被final修饰,其引用是不可变的,每次赋值都是一个新的引用地址,所以本质上仍然是隔离的。
深拷贝
- 复制对象的的所有基本数据类型的成员变量值;
- 为所有的引用数据类型成员变量申请存储空间,并复制每个引用类型成员变量所引用的对象,直到该对象可达的所有对象。
重写clone()实现深拷贝
/**
* 重写clone方法完成深拷贝
* @return
* @throws CloneNotSupportedException
*/
@Override
protected Person clone() throws CloneNotSupportedException {
Person person = (Person) super.clone();
person.setMoney((Money) person.getMoney().clone());
return person;
}
通过对象的序列化实现深拷贝
/**
* 通过序列化的形式实现深拷贝
* @return
*/
public Person 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 (Person) ois.readObject();
}catch (Exception e) {
System.out.println("对象深拷贝序列化异常");
} finally {
try {
if(bos != null) {
bos.close();
}
if(oos != null) {
oos.close();
}
if(bis != null) {
bis.close();
}
if(ois != null) {
ois.close();
}
}catch (Exception e) {
System.out.println("关闭序列化流异常");
}
}
return null;
}
对象属性在序列化和反序列化过程中完成元素的深拷贝。
建造者模式
建造者模式又叫生成器模式,是一种对象构建模式。它可以将复杂对象的构建过程抽象出来,使这个抽象过程的不同实现方法可以构造出不同表现的对象。
建造者模式是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建他们,用户不需要知道内部的具体构造细节。
建造者模式的四个角色
- Product(产品角色):一个具体的产品对象;
- Builder(抽象建造者):创建一个Product对象的各个部位指定的接口/抽象类;
- ConcreteBuilder(具体建造者):实现接口,构建和装配各个部件。
- Director(指挥者):构建一个使用Builder接口的对象。它主要是用于创建一个复杂的对象,它主要有两个作用:一是隔绝了客户与对象的生产过程,二是负责控制产品对象的生产过程。
适配器模式
适配器模式即将一个类的接口变成客户端所期待的另外一种接口,从而使原本因接口规则不匹配而无法在一起工作的两个类能够一起工作。
可以简单理解为针对单一功能的接口的拓展,比如默认的电压是220v,手机充电器的输入电压是5v,我们当然可以直接使用5v的类来完成功能,但是也可以使用适配器的模式将220v的电压转换为5v的电压来进行输出,此时完成转换这个过程的类就被称之为适配器。
类适配器模式
/**
* 默认电压,需要通过配置器增强的角色
*/
public class AC220 {
public static int output220(){
return 220;
}
}
/**
* 增强电压的多种模式
*/
public interface ACEnhance {
int output10v();
int output22v();
}
/**
* 实现增强的适配器
*/
public class Adapter extends AC220 implements ACEnhance{
@Override
public int output10v() {
return output220() / 22;
}
@Override
public int output22v() {
return output220() / 10;
}
}
实现原理:由于java单继承的特性,所以在此对适配目标的规定只能采用接口的形式进行。适配器继承需要适配的220v电压,并实现接口ACEnhance的方法,在适配器中实现适配功能。
接口适配器模式
接口适配器模式意思是在类适配器模式上的一个拓展,在类适配器模式中我们的适配目标接口ACEhance是一个万能接口,其中包含多个需要适配的目标。但是如果我们不想要实现所有的适配目标,可以在链路中添加一个抽象类来实现接口,实现内容空置,具体的实现由继承抽象类的适配器来实现。
/**
* 通过抽象类实现自由方法适配,避免适配器强制实现接口的所有方法
*/
public abstract class AbstractAdapter implements ACEnhance {
@Override
public int output10v() {
return 0;
}
@Override
public int output22v() {
return 0;
}
}
/**
* 实现增强的适配器
*/
public class Adapter extends AbstractAdapter{
/**
* 因为单继承,所以采用通过构造函数的形式来注入
*/
private final AC220 ac220;
public Adapter(AC220 ac220) {
this.ac220 = ac220;
}
@Override
public int output10v() {
return ac220.output220() / 22;
}
}
public class Test {
public static void main(String[] args) {
Adapter adapter = new Adapter(new AC220());
System.out.println(adapter.output22v());
System.out.println(adapter.output10v());
}
}
组合模式
组合模式是指将对象组合成树形结构以表示“整体-部分”的层次结构,组合模式使得操作单个对象和组合对象的具有一致性。
简单来说,组合模式可以将需要用层级关系来表示的元素组合成一个树形结构。主要实现方式是通过一个顶层抽象类来完成,树的子节点和叶子节点都在抽象类的限定下实现自己需要实现的功能,最终的目标是为了通过面向顶层抽象类来完成树状结构的构成。
/**
* 组合模式的顶层抽象
* 用于规定子节点(枝干节点)和叶子节点的所有功能
*/
public abstract class Component {
/**
* 通用的打印输出方法
*/
public abstract void print(String perStr);
/**
* 子节点所特有的方法
* 叶子节点无该方法,不被重写,如果被调用则抛出异常
* @param child 叶子节点
*/
public void addChild(Component child){
throw new RuntimeException("对象无使用该功能的权限");
}
/**
* 子节点所特有的方法
* 叶子节点无该方法,不被重写,如果被调用则抛出异常
* @param child 叶子节点
*/
public void removeChild(Component child){
throw new RuntimeException("对象无使用该功能的权限");
}
}
/**
* 子节点(枝干节点)
*/
public class Limb extends Component{
private final String name;
/**
* 子节点集合
*/
private List<Component> childs;
public Limb(String name){
this.name = name;
}
@Override
public void print(String preStr) {
/**
* 输出节点
* 如果是子节点,则还要递归输出下面的叶子节点
*/
System.out.println(preStr + "+" + name);
//子节点不为空则循环输出
if(null != childs) {
//输出子节点前的缩进
preStr += " ";
for (Component child : childs) {
child.print(preStr);
}
}
}
@Override
public void addChild(Component child) {
if(null == childs) {
childs = new ArrayList<>();
}
childs.add(child);
}
@Override
public void removeChild(Component child) {
if(null == childs) {
throw new RuntimeException("无子元素可以删除");
}
childs.remove(child);
}
}
/**
* 叶子节点
*/
public class Leaf extends Component {
private final String name;
public Leaf(String name) {
this.name = name;
}
@Override
public void print(String preStr) {
System.out.println(preStr + "-" + name);
}
}
public class Test {
public static void main(String[] args) {
Component root = new Limb("服饰");
Component woman = new Limb("女装");
Component man = new Limb("男装");
Component mother = new Limb("母婴");
Component skirt = new Leaf("女裙");
Component silk = new Leaf("丝袜");
Component motherSkirt = new Leaf("孕妇裙");
Component t = new Leaf("男士T恤");
Component manShoe = new Leaf("男鞋");
root.addChild(woman);
root.addChild(man);
woman.addChild(mother);
woman.addChild(skirt);
woman.addChild(silk);
mother.addChild(motherSkirt);
man.addChild(t);
man.addChild(manShoe);
root.print("");
}
}
代理模式
即静态代理模式,优势是可以保护被代理的目标和对被代理的目标进行增强,缺点是依靠额外的代理类,增加维护成本。
桥接模式
桥接模式主要是将抽象和实现解耦,使得两者之间都可以独立变化。
通过案例理解:例如要实现对手机这个内容的抽象,那么可以有多个维度来分析,比如手机的品牌或者手机的种类。桥接模式的首要步骤就是将多个维度的内容进行抽象,然后通过桥接的行为进行缝合使其可以任意拓展。
意思就是首先对手机的种类进行抽象,例如直板手机有打电话、按键的功能,智能手机有打电话、触屏的功能,然后我们还有小米和华为两个抽象的品牌。此时,就可以通过桥接模式实现小米的值班手机or小米的智能手机,或者是华为的直板手机or华为的智能手机。
/**
* 手机种类的抽象
*/
public abstract class BaseType {
/**
* 注入品牌的抽象
*/
protected BaseBrand baseBrand;
protected BaseType(BaseBrand baseBrand) {
this.baseBrand = baseBrand;
}
public void call() {
throw new RuntimeException("暂不支持该功能");
}
public void press() {
throw new RuntimeException("暂不支持该功能");
}
public void touchScreen() {
throw new RuntimeException("暂不支持该功能");
}
}
/**
* 手机品牌的抽象
*/
public interface BaseBrand {
void call();
void press();
void touchScreen();
}
/**
* 直板手机的实现
*/
public class BarPhone extends BaseType {
public BarPhone(BaseBrand baseBrand) {
super(baseBrand);
}
@Override
public void call() {
super.baseBrand.call();
System.out.print("---但是是直板手机");
}
@Override
public void press() {
super.baseBrand.press();
System.out.print("---但是是直板手机");
}
}
/**
* 智能手机的实现
*/
public class SmartPhone extends BaseType{
public SmartPhone(BaseBrand baseBrand) {
super(baseBrand);
}
@Override
public void call() {
super.baseBrand.call();
System.out.print("---但是是智能手机");
}
@Override
public void touchScreen() {
super.baseBrand.touchScreen();
System.out.print("---但是是智能手机");
}
}
public class XiaoMi implements BaseBrand{
@Override
public void call() {
System.out.print("小米打电话");
}
@Override
public void press() {
System.out.print("小米按按钮");
}
@Override
public void touchScreen() {
System.out.print("小米触屏");
}
}
public class HuaWei implements BaseBrand{
@Override
public void call() {
System.out.print("华为打电话");
}
@Override
public void press() {
System.out.print("华为按按钮");
}
@Override
public void touchScreen() {
System.out.print("华为触屏");
}
}
public class Test {
public static void main(String[] args) {
BarPhone xiaomiBarPhone = new BarPhone(new XiaoMi());
xiaomiBarPhone.call();
xiaomiBarPhone.press();
System.out.println("");
SmartPhone huaweiSmartPhone = new SmartPhone(new HuaWei());
huaweiSmartPhone.call();
huaweiSmartPhone.touchScreen();
}
}
装饰器模式
装饰器模式意味在不影响原有类的功能情况下对该类的能力进行拓展和增强。
代理模式也是出于同样的目的来实现的,只不过代理模式是采用实现子类的形式,耦合度高。装饰器的实现原理就是在装饰类中通过构造函数或者方法参数的形式传入被装饰的类,然后在装饰类中进行增强。
外观模式
外观模式即为子系统的一组接口提供一个统一的入口,入口由客户端使用,使得客户端在进行子系统调用的时候更加统一。
可以理解为当面临一个流程化的多个步骤的时候,我们一般需要创建多个对象实例来执行不同的方法来完成这个流程。这样操作的缺点是客户端调用过多的流程节点容易出错,并且流程中如果有接口发生变化的话客户端也要随之进行改变。
所谓外观模式,就是在这种流程化的过程外层封装一个外观,提供统一的出口给客户端调用,客户端不用关心流程内部变化,子系统流程发生更迭对于客户端来说也是无感的。
** 案例分析:**
** ** 假设一台电脑,它包含了 CPU(处理器),Memory(内存) ,Disk(硬盘)这几个部件,若想要启动电脑,则先后必须启动 CPU、Memory、Disk。关闭也‘
是如此。
但是实际上我们在电脑开/关机时根本不需要去操作这些组件,因为电脑已经帮我们都处理好了,并隐藏了这些东西。这些组件好比子系统角色,而电脑就是一个外观角色。
//子系统各部分
public class CPU {
public void startup(){
System.out.println("cpu startup!");
}
public void shutdown(){
System.out.println("cpu shutdown!");
}
}
public class Memory {
public void startup(){
System.out.println("memory startup!");
}
public void shutdown(){
System.out.println("memory shutdown!");
}
}
public class Disk {
public void startup(){
System.out.println("disk startup!");
}
public void shutdown(){
System.out.println("disk shutdown!");
}
}
//外观类
public class Computer {
private CPU cpu;
private Memory memory;
private Disk disk;
public Computer(){
cpu = new CPU();
memory = new Memory();
disk = new Disk();
}
public void startup(){
System.out.println("start the computer!");
cpu.startup();
memory.startup();
disk.startup();
System.out.println("start computer finished!");
}
public void shutdown(){
System.out.println("begin to close the computer!");
cpu.shutdown();
memory.shutdown();
disk.shutdown();
System.out.println("computer closed!");
}
}
//调用方式
Computer computer = new Computer();
computer.startup();
computer.shutdown();
享元模式
享元模式也叫蝇量模式,运用共享技术有效的支持大量细粒度的对象。
享元模式能解决重复对象的内存浪费问题,较经典的案例就是各种的池技术,例如数据连接池、缓存池等,需要对象的时候不用重复创建,直接从池中获取。
实例分析:
基于共享充电宝程序来分析享元模式,共享充电宝就类似一个充电宝的池,多个充电宝被使用的时候,并不是有一个用户就创建一个充电宝,而是在池中来操作已有的充电宝,来保证充电宝的复用性和内存的复用。
/**
* 充电宝的抽象
*/
public abstract class BasePortableBattery {
/**
* 充电宝的编号(名称)
*/
protected String name;
/**
* 标志充电宝是否正被使用
*/
protected boolean isUse;
/**
* 初始化充电宝的名字
*/
protected BasePortableBattery(String name){
this.name = name;
}
/**
* 放回充电宝
*/
public abstract void add();
/**
* 取出充电宝
*/
public abstract void get();
}
/**
* 具体的充电宝(罗马仕)
*/
public class Romanus extends BasePortableBattery {
public Romanus(String name) {
super(name);
}
@Override
public void add() {
System.out.println("罗马仕充电宝被放回!");
this.isUse = false;
}
@Override
public void get() {
System.out.println("罗马仕充电宝被取出");
this.isUse = true;
}
}
/**
* 具体的充电宝(小米)
*/
public class Xiaomi extends BasePortableBattery {
public Xiaomi (String name){
super(name);
}
@Override
public void add() {
System.out.println("小米充电宝被放回!");
this.isUse = false;
}
@Override
public void get() {
System.out.println("小米充电宝被取出");
this.isUse = true;
}
}
/**
* 获取充电宝的享元工厂
*/
public class PortableBatteryFactory {
/**
* 存放充电宝的池
*/
private static final List<BasePortableBattery> portableBatteryPool = new ArrayList<>();
/**
* 池的最大长度
*/
public static final int MAX = 2;
/**
* 使用静态代码块初始化池中的充电宝(最多只能放两个充电宝)
*/
static {
portableBatteryPool.add(new Romanus("romanus"));
portableBatteryPool.add(new Xiaomi("xiaomi"));
}
/**
* 随机获取一个未在使用中的充电宝
* @return
*/
public static BasePortableBattery getPortableBattery() {
for (BasePortableBattery portableBattery : portableBatteryPool) {
if(!portableBattery.isUse) {
portableBattery.get();
return portableBattery;
}
}
System.out.println("池已空,无充电宝可取出");
return null;
}
/**
* 获取一个指定名称且未在使用中的充电宝
* @param name
* @return
*/
public static BasePortableBattery getPortableBattery(String name) {
for (BasePortableBattery portableBattery : portableBatteryPool) {
if (portableBattery.name.equals(name) && !portableBattery.isUse) {
portableBattery.get();
return portableBattery;
}
}
System.out.println("无目标充电宝可取出");
return null;
}
/**
* 归还充电宝
*/
public static void addPortableBattery(BasePortableBattery battery) {
boolean useFlag = false;
for (BasePortableBattery portableBattery : portableBatteryPool) {
if (portableBattery.isUse) {
useFlag = true;
}
}
if(!useFlag) {
System.out.println("池已满,无法放回充电宝");
return;
}
boolean isMy = false;
for (BasePortableBattery portableBattery : portableBatteryPool) {
if (portableBattery.name.equals(battery.name)) {
portableBattery.add();
System.out.println("充电宝放回成功");
isMy = true;
}
}
if (!isMy) {
System.out.println("该充电宝不是由此处取出,无法放回");
}
}
}
模板模式
模板模式即在一个抽象类定义了执行它的方法的模板,它的子类可以重写方法实现。
实现原理:在抽象类中开放需要由子类个性化的方法给子类实现,但是模版方法可以使用final来修饰不让子类实现。那么,子类实现自己的定制化内容之后,就只能使用模版抽象类中的模版方法来决定流程中各个函数的执行顺序,也就是使用模版,但是可以由子类定制化流程中的部分内容。
命令模式
命令模式实现了行为请求者和行为实现者的解耦,可以理解为客户端在使用某个功能的时候只面向命令,而命令内部才决定由什么样的具体实现来完成,此过程对客户端来说是无感的。
从遥控器程序的设计上来思考,遥控器可以控制家庭中的多个家电,例如电灯、电冰箱、电视机。它们都有类似的功能,例如开机、关机等。虽然电冰箱、电视机的品牌型号各有不同,但是对于用户来说,他只需要关注遥控器中的命令,而命令才关心最后由怎么样的电器来完成功能。并且,从程序拓展的角度来思考,后续新的家电命令的添加是非常自由的,客户端只需要组合自己想要的命令就可以使用。
命令模式存在如下的几个关键文件角色:
- 命令抽象:由一个顶层的抽象来规定命令的功能;
- 命令的实现:例如电灯的打开命令,是一个具体的命令实现,并且在实现中要组合具体的实现者也就是电灯来完成命令;
- 实现者:命令的具体实现,例如电灯,可以实现电灯打开的命令;
- 命令的控制台:客户单可以直接操作命令来完成功能,也可以考虑引入控制台将类似的命令进行组合,实现统一管理。
案例解析:
通过实现遥控器程序来理解命令模式。
/**
* 命令的抽象
*/
public abstract class BaseCommand {
/**
* 命令执行:具体执行内容由命令的具体实现来决定
*/
public abstract void execute();
}
/**
* 具体的灯的实现
*/
public class LigtReceiver {
public void on() {
System.out.println("电灯打开");
}
public void off() {
System.out.println("电灯关闭");
}
}
/**
* 具体电视机的实现
*/
public class TvReceiver {
public void on() {
System.out.println("电视机打开");
}
public void off() {
System.out.println("电视机关闭");
}
}
/**
* 电灯打开命令
*/
public class LigtOnCommand extends BaseCommand {
private LigtReceiver ligtReceiver;
public LigtOnCommand(LigtReceiver ligtReceiver) {
this.ligtReceiver = ligtReceiver;
}
@Override
public void execute() {
ligtReceiver.on();
}
}
/**
* 电灯关闭命令
*/
public class LigtOffCommand extends BaseCommand{
private LigtReceiver ligtReceiver;
public LigtOffCommand(LigtReceiver ligtReceiver) {
this.ligtReceiver = ligtReceiver;
}
@Override
public void execute() {
ligtReceiver.off();
}
}
/**
* 电视机打开命令
*/
public class TvOnCommand extends BaseCommand {
private TvReceiver tvReceiver;
public TvOnCommand(TvReceiver tvReceiver) {
this.tvReceiver = tvReceiver;
}
@Override
public void execute() {
tvReceiver.on();
}
}
/**
* 电视机关闭命令
*/
public class TvOffCommand extends BaseCommand{
private TvReceiver tvReceiver;
public TvOffCommand(TvReceiver tvReceiver) {
this.tvReceiver = tvReceiver;
}
@Override
public void execute() {
tvReceiver.off();
}
}
/**
* 命令的集成控制
*/
public class CommandController {
private static Map<String, Map<String, BaseCommand>> commandPool = new HashMap<>();
/**
* 初始化控制器(也可以将初始化控制器的能力交给客户端来自由组合)
*/
static {
Map<String, BaseCommand> ligtCommand = new HashMap<>();
ligtCommand.put("on", new LigtOnCommand(new LigtReceiver()));
ligtCommand.put("off", new LigtOffCommand(new LigtReceiver()));
commandPool.put("ligt", ligtCommand);
Map<String, BaseCommand> tvCommand = new HashMap<>();
tvCommand.put("on", new TvOnCommand(new TvReceiver()));
tvCommand.put("off", new TvOffCommand(new TvReceiver()));
commandPool.put("tv", tvCommand);
}
public static Map<String, Map<String, BaseCommand>> getCommandPool() {
return commandPool;
}
}
public class Test {
public static void main(String[] args) {
Map<String, Map<String, BaseCommand>> commandPool = CommandController.getCommandPool();
commandPool.get("tv").get("on").execute();
}
}
访问者模式
访问者模式主要将数据结构和数据操作分离,解决数据结构和操作耦合性的问题。
简单理解访问者模式:我认为访问者模式的重要性在于实现了数据单元和操作过程的分离,以下面实例的男性、女性不同群体的评价功能为例,男性女性都有评价成功和评价失败的操作。在这个案例中,男性女性这个具体的群体就是数据单元,他们的集合产生了一个数据结构 ,但是男性女性在做同样的事情的时候可能会有不同的表现,例如男性直接投票,女性会鼓掌然后投票。在这个过程中,访问者模式可以在不修改数据单元和数据结构的代码下,在操作中实现不同数据单元产生不同的结果。
案例解析
/**
* 访问行为的顶层抽象
*/
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 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> personList = new ArrayList<>();
/**
* 新增数据单元到集合
*/
public void attach(Person person){
personList.add(person);
}
/**
* 从集合中删除数据单元
*/
public void detach(Person person){
personList.remove(person);
}
/**
* 显示评价结果
*/
public void display(Action action) {
personList.forEach(person -> {
person.accept(action);
});
}
}
public class Test {
public static void main(String[] args) {
ObjectStructure structure = new ObjectStructure();
structure.attach(new Man());
structure.attach(new WoMan());
structure.display(new Success());
}
}
迭代器模式
迭代器模式提供一种遍历集合元素的统一接口,用一致的方法遍历集合元素,不需要知道集合对象的底层表示,即不暴露其内部的结构。
可以理解为如果我们的集合元素使用不同的方式实现的,例如可能是数组或者java的集合类,当客户端要遍历这些集合元素的时候就要使用多种遍历方式,而且还会暴露元素内部的结构,迭代器模式就可以解决这个问题。
案例解析
/**
* 迭代器的抽象
*/
public abstract class Iterator {
public abstract boolean hasNext();
public abstract Object next();
}
/**
* 迭代器的实现
*/
public class StudentIterator extends Iterator {
private final List<Student> list;
private int position = 0;
public StudentIterator(List<Student> list) {
this.list = list;
}
@Override
public boolean hasNext() {
return position < list.size();
}
@Override
public Student next() {
Student student = list.get(position);
position++;
return student;
}
}
/**
* 抽象聚合类(通过功能聚合对象和迭代器)
*/
public abstract class Aggregate {
public abstract void add(Object ob);
public abstract void remove(Object ob);
public abstract Iterator getIterator();
}
/**
* Student聚合Iterator的实现
*/
public class StudentAggregate extends Aggregate{
private final List<Student> list;
public StudentAggregate(List<Student > list) {
this.list = list;
}
@Override
public void add(Object ob) {
list.add((Student) ob);
}
@Override
public void remove(Object ob) {
list.remove((Student) ob);
}
@Override
public Iterator getIterator() {
return new StudentIterator(list);
}
}
观察者模式
观察者模式也叫发布订阅模式,即在对象中定义一对多的依赖,当对象发生变化的时候所有依赖的对象都会得到通知并自动更新。
简单来说,就是在被观察者对象中维护了一个观察者集合,在被观察者发生变化的时候循环通知观察者集合中的每个对象。
案例解析
/**
* 观察者抽象
*/
public interface Observer {
void response();
}
/**
* 父亲观察者实现
*/
public class FatherObserver implements Observer{
private final String name;
public FatherObserver (String name) {
this.name = name;
}
@Override
public void response() {
System.out.println(name + "走过来了");
}
}
/**
* 母亲观察者实现
*/
public class MotherObserver implements Observer{
private final String name;
public MotherObserver (String name) {
this.name = name;
}
@Override
public void response() {
System.out.println(name + "走过来了");
}
}
/**
* 被观察者抽象
*/
public abstract class Subject {
/**
* 观察者集合
*/
private final List<Observer> observers = new ArrayList<>();
/**
* 新增观察者
* @param observer 观察者实现
*/
public void addObserver(Observer observer) {
observers.add(observer);
}
/**
* 删除观察者
* @param observer 观察者实现
*/
public void removeObserver(Observer observer) {
observers.remove(observer);
}
/**
* 通知观察者
*/
public void notice() {
observers.forEach(Observer::response);
}
}
/**
* 小孩被观察者实现
*/
public class ChildSubject extends Subject{
private final String name;
public ChildSubject (String name) {
this.name = name;
}
@Override
public void notice() {
System.out.println(name + "哭了");
super.notice();
}
}
public class Test {
public static void main(String[] args) {
FatherObserver fatherObserver = new FatherObserver("父亲");
MotherObserver motherObserver = new MotherObserver("母亲");
ChildSubject childSubject = new ChildSubject("小孩");
childSubject.addObserver(fatherObserver);
childSubject.addObserver(motherObserver);
childSubject.notice();
}
}
中介者模式
中介者模式即定义一个中介者角色来封装一系列对象之间的交互,使原有的对象直接交互的关系松耦合。
举例说明:例如房东和租客之间的关系,房东和租客的沟通是可以直接进行的,但是房东可能会和多个租客进行沟通,租客也可能有其他的例如领导、同事要进行交互。此时在优化过程中就可以封装一个中介角色,由中介角色来管理房东和租客的对应关系,并沟通搭建两端的交互,这样房东和租客就只需要面对中介这一个角色。
案例解析
/**
* 中介的抽象
*/
public abstract class AbstractMediator {
public abstract void contact(String message, Person person);
}
/**
* 中介的实现
*/
public class Mediator extends AbstractMediator {
private Person landlord;
private Person tenant;
public void setLandlord(Person landlord) {
this.landlord = landlord;
}
public void setTenant(Person tenant) {
this.tenant = tenant;
}
@Override
public void contact(String message, Person person) {
if (person.name.equals(landlord.name)) {
tenant.getMessage(message);
} else {
landlord.getMessage(message);
}
}
}
/**
* 客户的抽象
*/
public abstract class Person {
protected String name;
protected AbstractMediator mediator;
protected Person (String name, AbstractMediator mediator){
this.name = name;
this.mediator = mediator;
}
public abstract void contact(String message);
public abstract void getMessage(String message);
}
/**
* 房东的实现
*/
public class Landlord extends Person {
public Landlord (String name, AbstractMediator mediator) {
super(name, mediator);
}
@Override
public void contact(String message) {
this.mediator.contact(message, this);
}
@Override
public void getMessage(String message) {
System.out.println("房东收到的消息是:" + message);
}
}
/**
* 租客的实现
*/
public class Tenant extends Person{
public Tenant (String name, AbstractMediator mediator) {
super(name, mediator);
}
@Override
public void contact(String message) {
this.mediator.contact(message, this);
}
@Override
public void getMessage(String message) {
System.out.println("租客收到的消息是:" + message);
}
}
public class Test {
public static void main(String[] args) {
Mediator mediator = new Mediator();
Landlord landlord = new Landlord("房东", mediator);
Tenant tenant = new Tenant("租客", mediator);
mediator.setLandlord(landlord);
mediator.setTenant(tenant);
tenant.contact("我想租房");
landlord.contact("我这里有房");
}
}
备忘录模式
备忘录模式即为对象创建一个备忘录类,并提供回滚信息的能力。简单理解来说,备忘录类就是目标对象的备份,记录了目标对象之前的状态,当目标对象发生变化后想要吃后悔药就可以通过回滚将备忘录中的信息复制回去。
案例解析
/**
* 备忘录类
*/
@Data
@AllArgsConstructor
public class Memento {
private String state;
}
/**
* 发起人(被备份的类)
*/
@Data
@AllArgsConstructor
public class Originator {
private String state;
/**
* 创建一个备忘录
* @return 备忘录
*/
public Memento createMemento() {
return new Memento(state);
}
/**
* 通过备忘录撤销
*/
public void revocationMemento(Memento memento) {
this.state = memento.getState();
}
}
/**
* 管理者
*/
public class Caretaker {
private Memento memento;
/**
* 添加备忘录
* @param memento 备忘录
*/
public void addMemento(Memento memento){
this.memento = memento;
}
/**
* 获取备忘录
* @return
*/
public Memento getMemento(){
return memento;
}
}
public class Test {
public static void main(String[] args) {
Originator originator = new Originator("成功");
Caretaker caretaker = new Caretaker();
caretaker.addMemento(originator.createMemento());
System.out.println(originator.getState());
originator.setState("失败");
System.out.println(originator.getState());
originator.revocationMemento(caretaker.getMemento());
System.out.println(originator.getState());
}
}
解释器模式
解释器模式是一个对象的行为模式,给定一个语句,定义它文法的一种表示,并定义一个解释器,这个解释器就使用该表示来解释语言中的句子。
通过简单加减表达式来通俗的理解解释器的工作原理,针对一个加减表达式"a+b+c"来讲,表达式就是给定的语句,文法的表示就是语句中的加减符号,解释器可以通过解析语句,并根据文法表示来解释出语句的意思,也就是通过加减符号计算出表达式的数值。
案例解析
/**
* 抽象表达式,声明解释操作
*/
public interface AbstractExpression {
/**
* 每个表达式都必须有一个解释操作
*/
public int interpreter(HashMap<String, Integer> var);
}
/**
* 终结符表达式,代表参加运算的元素对象
*/
public class VarExpression implements AbstractExpression {
private String key;
public VarExpression(String key) {
this.key = key;
}
@Override
public int interpreter(HashMap<String, Integer> var) {
return (Integer) var.get(this.key);
}
}
/**
* 非终结符表达式,运算符(此处为加法和减法)的抽象父类,真正的解释操作由其子类来实现
*/
public abstract class SymbolExpression implements AbstractExpression {
protected AbstractExpression left;
protected AbstractExpression right;
/**
* 非终结符表达式的解释操作只关心自己左右两个表达式的结果
*/
public SymbolExpression(AbstractExpression left, AbstractExpression right) {
this.left = left;
this.right = right;
}
}
/**
* 加法表达式
*/
public class AddExpression extends SymbolExpression {
public AddExpression(AbstractExpression left, AbstractExpression right) {
super(left, right);
}
/**
* 把左右两个表达式运算的结果加起来
*/
@Override
public int interpreter(HashMap<String, Integer> var) {
return super.left.interpreter(var) + super.right.interpreter(var);
}
}
/**
* 减法表达式
*/
public class SubExpression extends SymbolExpression {
public SubExpression(AbstractExpression left, AbstractExpression right) {
super(left, right);
}
/**
* 左右两个表达式相减
*/
@Override
public int interpreter(HashMap<String, Integer> var) {
return super.left.interpreter(var) - super.right.interpreter(var);
}
}
public class Calculator {
private AbstractExpression expression;
/**
* 对公式进行解析操作
*/
public Calculator(String expStr) {
// 定义一个堆栈,安排运算的先后顺序
Stack<AbstractExpression> stack = new Stack<>();
// 表达式拆分为字符数组
char[] charArray = expStr.toCharArray();
// 运算
AbstractExpression left = null;
AbstractExpression 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 SubExpression(left, right));
break;
default:
stack.push(new VarExpression(String.valueOf(charArray[i])));
}
}
// 把运算结果抛出来
this.expression = stack.pop();
}
/**
* 计算结果
*/
public int calculate(HashMap<String, Integer> var) {
return this.expression.interpreter(var);
}
}
public class Test {
public static void main(String[] args) {
// 构造运算元素的值列表
HashMap<String, Integer> ctx = new HashMap<String, Integer>();
ctx.put("a", 10);
ctx.put("b", 20);
ctx.put("c", 30);
ctx.put("d", 40);
ctx.put("e", 50);
ctx.put("f", 60);
Calculator calc = new Calculator("a+b-c");
int result = calc.calculate(ctx);
System.out.println("Result of a+b-c: " + result);
calc = new Calculator("d-a-b+c");
result = calc.calculate(ctx);
System.out.println("Result of d-a-b+c: " + result);
}
}
状态模式
状态模式主要用来解决程序中繁复的if-else情况,当我们的代码中有非常多的根据何种状态执行何种代码的行为,就会出现非常臃肿繁杂的if语句,此时可以考虑使用状态模式来封装分支行为。
封装分支行为的意思就是将不同的状态对应的分支代码进行封装,这样当后续需要进行状态分支的代码修改或者内容新增的时候,只需要修改对应的封装好的分支行为,不会影响到其他的分支。
策略模式
个人理解,策略模式和状态模式都是做一样的事情,具体待后续资料探究。
责任链模式
责任链模式是针对请求构建了一条多个类处理的链路,在某些需要多级程序处理的场景中使用。例如请假流程,当请假一天的时候,由组长审批,当请假大于三天的时候,除组长外需要主管审批,当请假大于七天的时候,除组长和主管外还需要经理审批。在这个链路中,请假的行为就可能涉及到多个角色的处理,可以采用责任链的模式进行开发。
案例解析
/**
* 请假处理器的抽象
*/
public abstract class AbstractProcessor {
protected String name;
protected AbstractProcessor (String name) {
this.name = name;
}
public abstract void examine (String staffName, int num) ;
}
public class GroupProcessor extends AbstractProcessor {
private final ChargeProcessor chargeProcessor = new ChargeProcessor("主管");
public GroupProcessor(String name) {
super(name);
}
@Override
public void examine(String staffName, int num) {
System.out.println(this.name + "已同意员工" + staffName + "的请假申请");
if (num > 1) {
chargeProcessor.examine(staffName, num);
}
}
}
public class ChargeProcessor extends AbstractProcessor {
private final ManagerProcessor managerProcessor = new ManagerProcessor("经理");
public ChargeProcessor(String name) {
super(name);
}
@Override
public void examine(String staffName, int num) {
System.out.println(this.name + "已同意员工" + staffName + "的请假申请");
if (num > 3) {
managerProcessor.examine(staffName, num);
}
}
}
public class ManagerProcessor extends AbstractProcessor{
public ManagerProcessor(String name) {
super(name);
}
@Override
public void examine(String staffName, int num) {
System.out.println(this.name + "审批意见:太几把长了,不准请");
}
}
/**
* 员工类
*/
@Data
@AllArgsConstructor
public class Staff {
private String name;
/**
* 员工发起请假审批
*/
public void sponsor(int num, AbstractProcessor processor) {
System.out.println("员工:" + name + "发起请假审批,请假时间为:" + num + "天");
processor.examine(name, num);
}
}
public class Test {
public static void main(String[] args) {
Staff staff = new Staff("张三");
staff.sponsor(7, new GroupProcessor("组长"));
}
}
设计模式,暂到此为止......
标签:name,void,class,new,设计模式,public,String From: https://www.cnblogs.com/xiashihua/p/16941482.html