设计模式之结构型模式

适配器模式

适配器模式有两种实现方式

类适配器采用继承

//MicroUSB 
package com.std.www.designPattern.adapter;

public class MicroUSB {
    public void recharge(){
        System.out.println("使用Micro USB接口进行充电");
    }
}

//Plug
package com.std.www.designPattern.adapter;

public interface Plug {
    void connectOutlet();
}

//Adapter
package com.std.www.designPattern.adapter;

public class Adapter extends MicroUSB implements Plug{
    @Override
    public void connectOutlet() {
        System.out.println("连接到插座");
        super.recharge();
    }
}

//Phone
package com.std.www.designPattern.adapter;

public class Phone {
    public void recharge(Plug adapter){
        adapter.connectOutlet();
    }
}

//Client
package com.std.www.designPattern.adapter;

public class Client {
    public static void main(String[] args) {
        Phone phone=new Phone();
        Adapter adapter=new Adapter();
        phone.recharge(adapter);

    }
}

手机无法直接和插座相连进行充电,通过适配器关联Phone和Plug让手机可以正常充电,但是这样只能局限于USB接口的手机,我们应该让适配的接口多样化

对象适配器采用组合

//ChargePort
package com.std.www.designPattern.adapter;

public interface ChargePort {
    void recharge();
}

//Lightning
package com.std.www.designPattern.adapter;

public class Lightning implements ChargePort{
    @Override
    public void recharge(){
        System.out.println("使用Lightning接口进行充电");
    }
}

//MicroUSB
package com.std.www.designPattern.adapter;

public class MicroUSB implements ChargePort{
    @Override
    public void recharge(){
        System.out.println("使用Micro USB接口进行充电");
    }
}

//TypeC
package com.std.www.designPattern.adapter;

public class TypeC implements ChargePort{
    @Override
    public void recharge(){
        System.out.println("使用Type-C接口进行充电");
    }
}

//Plug
package com.std.www.designPattern.adapter;

public interface Plug {
    void connectOutlet();
}

//Adapter
package com.std.www.designPattern.adapter;

public class Adapter implements Plug{
    private ChargePort chargePort;

    public Adapter(ChargePort chargePort) {
        this.chargePort = chargePort;
    }

    public Adapter() {
    }

    public ChargePort getChargePort() {
        return chargePort;
    }

    public void setChargePort(ChargePort chargePort) {
        this.chargePort = chargePort;
    }

    @Override
    public void connectOutlet() {
        System.out.println("连接到插座");
        if(chargePort!=null)
        chargePort.recharge();
        else System.out.println("接口已被拔除");
    }
}

//Phone
package com.std.www.designPattern.adapter;

public class Phone {
    public void recharge(Plug adapter){
        adapter.connectOutlet();
    }
}

//Client
package com.std.www.designPattern.adapter;

public class Client {
    public static void main(String[] args) {
        Phone phone=new Phone();
        ChargePort chargePort=null;
        Adapter adapter=new Adapter();
        chargePort=new Lightning();
        adapter.setChargePort(chargePort);
        phone.recharge(adapter);
        chargePort=new MicroUSB();
        adapter.setChargePort(chargePort);
        phone.recharge(adapter);
        chargePort=new TypeC();
        adapter.setChargePort(chargePort);
        phone.recharge(adapter);
        adapter.setChargePort(null);
        phone.recharge(adapter);

    }
}

可以看出相比于类适配器,对象适配器不仅实现了接口的拔插,还实现了多接口机制,因为java中继承只能是单继承所以无法实现多接口

适配器模式一般有一个Adaptee为需要适配的接口,一个Target为Client需要使用的接口,通过Adapter类将Adaptee和Target关联起来,例如上方例子中手机无法直接连接到Plug进行充电,必须通过Adapter才行,这里Plug为Target,Adapter为适配器,三个接口为Adaptee适配者,Phone为客户端

主要角色:

  1. 目标接口(Target): 客户端所期望的接口,适配器模式通过适配器将其他接口转换成目标接口。

  2. 适配器(Adapter): 将其他类或接口转换成目标接口的类。它包含一个对被适配者的引用,通过调用被适配者的方法来实现目标接口。

  3. 被适配者(Adaptee): 需要被适配的类或接口。它定义了客户端不关心的方法,但是适配器需要将这些方法适配成目标接口的方法。

特点:

  • 兼容性: 适配器模式用于使原本不兼容的接口能够协同工作。

  • 解耦性: 通过适配器,客户端和被适配者可以独立演化,互不影响。

优点:

  • 复用性: 可以将现有的类或接口加入到适配器,使得这些类或接口能够与其他代码协同工作。

  • 透明性: 客户端通过目标接口与适配器交互,对于客户端来说,适配器是透明的,客户端不需要知道被适配者的具体实现。

缺点:

  • 过多的适配器: 如果系统中存在大量的适配器,可能会导致系统变得复杂难以理解。

适用场景:

  • 需要使用已经存在的类,但是其接口不符合需求的情况。

  • 想要创建一个可复用的类,该类可以与其他不相关的类或不可预见的类(即没有提前设计的接口)协同工作。

桥接模式

//Brand
package com.std.www.designPattern.bridge;

public interface Brand {
    void showInfo();
}

//AppleBrand
package com.std.www.designPattern.bridge;

public class AppleBrand implements Brand{
    @Override
    public void showInfo() {
        System.out.print("苹果");
    }
}

//HuaweiBrand
package com.std.www.designPattern.bridge;

public class HuaweiBrand implements Brand{
    @Override
    public void showInfo() {
        System.out.print("华为");
    }
}

//SamsungBrand

package com.std.www.designPattern.bridge;

public class SamsungBrand implements Brand{
    @Override
    public void showInfo() {
        System.out.print("三星");
    }
}

//Computer

package com.std.www.designPattern.bridge;

public abstract class Computer {
    protected Brand brand;

    public Computer(Brand brand) {
        this.brand = brand;
    }
    public void showInfo(){
        brand.showInfo();
    }
}

//DesktopComputer

package com.std.www.designPattern.bridge;

public class DesktopComputer extends Computer {
    public DesktopComputer(Brand brand) {
        super(brand);
    }

    @Override
    public void showInfo() {
        super.showInfo();
        System.out.println("台式电脑");
    }
}

//FlatComputer

package com.std.www.designPattern.bridge;

public class FlatComputer extends Computer{
    public FlatComputer(Brand brand) {
        super(brand);
    }
    @Override
    public void showInfo() {
        super.showInfo();
        System.out.println("平板电脑");
    }
}

//LaptopComputer

package com.std.www.designPattern.bridge;

public class LaptopComputer extends Computer{
    public LaptopComputer(Brand brand) {
        super(brand);
    }
    @Override
    public void showInfo() {
        super.showInfo();
        System.out.println("笔记本电脑");
    }
}

//Client

package com.std.www.designPattern.bridge;

public class Client {
    public static void main(String[] args) {
        Computer computer=new DesktopComputer(new AppleBrand());
        computer.showInfo();
        computer=new LaptopComputer((new HuaweiBrand()));
        computer.showInfo();
        computer=new FlatComputer(new SamsungBrand());
        computer.showInfo();
    }
}

桥接模式实现了抽象和实现部分分离,避免了多层继承的方式,一般含有一个抽象类作为桥,一个接口类和抽象类进行聚合,通过该桥联系抽象类的具体实现和接口类的具体实现,例如上方Computer为桥,Brand为接口类,我想要得到不同品牌的不同样式的电脑,传统方法是定义一个电脑类,通过继承实现多个样式的电脑,对于每个样式再通过继承得到各个品牌,这样不符合单一原则

桥接模式通过Computer为桥将具体的电脑样式和电脑品牌连接起来,使得具体样式和具体品牌之间隔离互不干扰,如果后续实现了新的品牌,只需传入即可得到新品牌下的各种样式的电脑,类似一个二维表格,大大减少了子类的个数

主要角色:

  1. 抽象类(Abstraction): 定义抽象部分的接口,并包含一个对实现部分的引用。

  2. 扩展抽象类(Refined Abstraction): 对抽象类的扩展,可以为抽象部分添加新的行为。

  3. 实现接口(Implementor): 定义实现部分的接口,该接口不一定与抽象部分的接口完全一致。

  4. 具体实现类(Concrete Implementor): 实现实现接口,提供具体的实现。

特点:

  • 解耦性: 将抽象部分与实现部分分离,使它们可以独立变化,减少它们之间的依赖关系。

优点:

  • 可扩展性: 抽象部分和实现部分可以独立扩展,系统更容易扩展。

  • 变化的隔离: 可以对抽象部分和实现部分进行独立的变化,不影响对方。

缺点:

  • 增加复杂性: 桥接模式增加了系统的复杂性,因为它需要对抽象部分和实现部分进行设计。

适用场景:

  • 当一个类存在两个独立变化的维度,且需要在这两个维度上进行扩展时,使用桥接模式。

  • 当一个类需要对多个实现进行选择时,可以使用桥接模式。

装饰者模式

//Cake
package com.std.www.designPattern.decorator;

public interface Cake {
    int cost();
    void showIngredients();
}

//ChiffonCake
package com.std.www.designPattern.decorator;

public class ChiffonCake implements Cake{

    @Override
    public int cost() {
        System.out.print(" 10$ ");
        return 10;
    }

    @Override
    public void showIngredients() {
        System.out.print("戚风蛋糕");
    }
}

//SpongeCake
package com.std.www.designPattern.decorator;

public class SpongeCake implements Cake{
    @Override
    public int cost() {
        System.out.print(" 20$ ");
        return 20;
    }

    @Override
    public void showIngredients() {
        System.out.print("海绵蛋糕");
    }
}

//Decorator
package com.std.www.designPattern.decorator;

public abstract class Decorator implements Cake{
    private Cake cake;

    public Decorator(Cake cake) {
        this.cake = cake;
    }

    @Override
    public int cost() {
        return cake.cost();
    }

    @Override
    public void showIngredients() {
        cake.showIngredients();
    }
}

//ChocolateSauce
package com.std.www.designPattern.decorator;

public class ChocolateSauce extends Decorator{
    public ChocolateSauce(Cake cake) {
        super(cake);
    }

    @Override
    public int cost() {
        System.out.print(" 15$ ");
        return super.cost()+15;
    }

    @Override
    public void showIngredients() {
        System.out.print("巧克力酱");
        super.showIngredients();
    }
}

//StrawberryJam
package com.std.www.designPattern.decorator;

public class StrawberryJam extends Decorator{
    public StrawberryJam(Cake cake) {
        super(cake);
    }

    @Override
    public int cost() {
        System.out.print(" 25$ ");
        return super.cost()+25;
    }

    @Override
    public void showIngredients() {
        System.out.print("草莓酱");
        super.showIngredients();
    }
}

//Client
package com.std.www.designPattern.decorator;

public class Client {
    public static void main(String[] args) {
        Cake cake=new SpongeCake();
        cake.showIngredients();
        System.out.println("\n总价"+cake.cost());
        cake=new ChocolateSauce(cake);
        cake.showIngredients();
        System.out.println("\n总价"+cake.cost());
        cake=new StrawberryJam(cake);
        cake.showIngredients();
        System.out.println("\n总价"+cake.cost());
    }
}

装饰者模式一般都有一个抽象组件Component和一个抽象装饰者类Decorator,Decorator继承Component,并且内部还包含一个Component字段,具体装饰实现类需要先构造父类中的Component,这个父类中的Component是之前被层层装饰完后的,然后再累加上该装饰者的装饰,具体点来说,就像例子里面的我们现在有一个Cake想要为其装饰各种各样的酱料,一般我们都是把酱料放到Cake中,这样就导致多层继承,随着组合方式的不同会产生各种各样的蛋糕类,于是乎装饰者模式就出现了,即我们不需要改动Cake本身,即不是往Cake里面添加酱料而是往酱料中投放Cake,例如把Cake放入巧克力酱中再拿出来这样就是一个包裹一层巧克力酱的Cake,再放入草莓酱中,这样外面就又包裹了一层草莓酱,这种层层包裹就类似于多层继承,但却没有写成多层继承的形式极大的减少了子类个数,这同时可以传入其它类型的蛋糕对其进行装饰

从对象的角度来看,就是用装饰完的对象更新Decorator中的Component,子类调用的就是装饰好后的对象,然再对其进行装饰,这样就完成了装饰的累加,体现了OCP原则,即没有改变组件结构但是动态地添加了新的功能

主要角色:

  1. 组件(Component): 定义一个抽象接口,可以给这些对象动态地添加职责。

  2. 具体组件(ConcreteComponent): 实现组件的具体对象,是被装饰的对象。

  3. 装饰者(Decorator): 持有一个指向组件对象的引用,并定义一个与组件接口一致的接口。

  4. 具体装饰者(ConcreteDecorator): 对组件进行具体的装饰,负责添加新的职责。

特点:

  • 装饰者模式通过嵌套组合,可以动态地将责任附加到对象上。

  • 装饰者模式遵循开闭原则,允许在不修改现有代码的情况下引入新的功能。

优点:

  • 可以灵活地扩展对象的功能,而不需要继承。

  • 允许对一个对象的各个部分进行独立的扩展和修改,避免了类爆炸的问题。

缺点:

  • 增加了许多具体装饰类,可能会使系统变得复杂。

适用场景:

  • 当需要动态地给一个对象添加功能,而且这些功能可以动态组合时,使用装饰者模式。

  • 当不能采用继承方式对系统进行扩展或者不希望采用继承方式时,可以使用装饰者模式。

  • 当要求保持类的封装性,避免在类中直接添加新的功能,以防止影响其他类时,可以使用装饰者模式。

组合模式

//Tableware
package com.std.www.designPattern.composite;

public abstract class Tableware {
    public void add(Tableware tableware){

    }
    public void remove(Tableware tableware){

    }
    public Tableware getChild(int i){
       return null;
    }
    public abstract void showInfo();

}

//Bowl
package com.std.www.designPattern.composite;

public class Bowl extends Tableware{

    @Override
    public void showInfo() {
        System.out.print(" 碗 ");
    }
}

//Plate
package com.std.www.designPattern.composite;

import java.util.ArrayList;
import java.util.List;

public class Plate extends Tableware{
    List<Tableware> tablewareList=new ArrayList<>();
    @Override
    public void add(Tableware tableware) {
        tablewareList.add(tableware);
    }

    @Override
    public void remove(Tableware tableware) {
        tablewareList.remove(tableware);
    }

    @Override
    public Tableware getChild(int i) {
        return tablewareList.get(i);
    }

    @Override
    public void showInfo() {
        System.out.print(" 盘子 ");
        if(tablewareList.size()>0){
            System.out.print("包含:{");
            for (Tableware tableware : tablewareList) {
                tableware.showInfo();
            }
            System.out.print("}");
        }
    }
}

//Chopsticks
package com.std.www.designPattern.composite;

public class Chopsticks extends Tableware{
    @Override
    public void showInfo() {
        System.out.print(" 筷子 ");
    }
}

//Fork
package com.std.www.designPattern.composite;

public class Fork extends Tableware{
    @Override
    public void showInfo() {
        System.out.print(" 叉子 ");
    }
}


//Cup

package com.std.www.designPattern.composite;

public class Cup extends Tableware{

    @Override
    public void showInfo() {
        System.out.print(" 茶杯 ");
    }
}

//Table

package com.std.www.designPattern.composite;

import java.util.ArrayList;
import java.util.List;

public class Table extends Tableware{
    private List<Tableware> tablewareList=new ArrayList<>();
    @Override
    public void add(Tableware tableware) {
        tablewareList.add(tableware);
    }

    @Override
    public void remove(Tableware tableware) {
        tablewareList.remove(tableware);
    }

    @Override
    public Tableware getChild(int i) {
        return tablewareList.get(i);
    }

    @Override
    public void showInfo() {
        System.out.print("餐桌");
        if(tablewareList.size()>0){
            System.out.print("包含:");
            for (Tableware tableware : tablewareList) {
                tableware.showInfo();
            }
        }
        System.out.print("\n");
    }
}

//Client

package com.std.www.designPattern.composite;

public class Client {
    public static void main(String[] args) {
        Tableware table=new Table();
        Tableware plate=new Plate();
        plate.showInfo();
        System.out.println();
        plate.add(new Chopsticks());
        plate.showInfo();
        plate.add(new Fork());
        System.out.println();
        plate.showInfo();
        table.add(plate);
        table.add(new Cup());
        table.add(new Bowl());
        System.out.println();
        table.showInfo();
    }
}

组合模式一般有一个抽象构建类Component,具体实现类有两种一种为Leaf即叶子构件,一种为Composite即容器构件,用例子里面来介绍其关系,抽象类为餐具类,其余都是它的子类,其中餐桌类为Composite,其内部含有一个Component的容器字段,可以增加删除Component,而其余的像碗盘子等为Leaf,一般都是容器构件可以包含叶子构件和容器构件,最形象生动的就是文件和文件夹了,定义一个抽象的文件,子类有具体文件和文件夹,文件夹下可以包含文件和文件夹

这就是组合模式,引入一个叶子和容器共同的父类,从而形成了树形结构

主要角色:

  1. 组件(Component): 定义一个抽象接口,声明了在组合中所有具体对象和组件的共同接口。

  2. 叶子(Leaf): 在组合中表示叶子节点对象,它没有子节点。

  3. 合成(Composite): 定义有子部件的那些部件的行为,并存储子部件。

特点:

  • 将对象组织成树状结构,使得客户端可以统一处理单个对象和组合对象。

  • 组合模式让客户端代码更加一致,无论处理单个对象还是对象组合。

优点:

  • 客户端代码可以一致地对待单个对象和组合对象,简化了客户端代码。

  • 新增类型的组件很容易,无需修改现有的代码。

  • 提供了清晰的层次结构,使得更容易对整个结构进行操作。

缺点:

  • 可能会导致设计中类的数量增加,因为需要定义叶子和组合对象。

适用场景:

  • 当希望客户端代码能够统一处理单个对象和组合对象时,可以使用组合模式。

  • 当希望将树形结构的对象组织起来表示部分-整体关系,并且希望客户端能够一致地对待单个对象和组合对象时,可以使用组合模式。

  • 当希望新增类型的组件时不影响客户端代码时,可以使用组合模式。

外观模式 

//Computer
package com.std.www.designPattern.facade;

public class Computer {
    public void start(){
        System.out.println("打开电脑");
    }
    public void play(){
        System.out.println("电脑播放");
    }
    public void pause(){
        System.out.println("电脑暂停");
    }
    public void close(){
        System.out.println("关闭电脑");
    }
}

//Fan
package com.std.www.designPattern.facade;

public class Fan {
    public void start(){
        System.out.println("打开风扇");
    }
    public void close(){
        System.out.println("关闭风扇");
    }
}

//Hvac
package com.std.www.designPattern.facade;

public class Hvac {
    public void start(){
        System.out.println("打开空调");
    }
    public void close(){
        System.out.println("关闭空调");
    }
}

//Lamp
package com.std.www.designPattern.facade;

public class Lamp {
    public void start(){
        System.out.println("打开电灯");
    }
    public void close(){
        System.out.println("关闭电灯");
    }
}

//TV
package com.std.www.designPattern.facade;

public class TV {
    public void start(){
        System.out.println("打开电视");
    }
    public void play(){
        System.out.println("电视播放");
    }
    public void pause(){
        System.out.println("电视暂停");
    }
    public void close(){
        System.out.println("关闭电视");
    }
}


//Facade
package com.std.www.designPattern.facade;

public interface Facade {
    void start();
    void close();
    void play();
    void pause();
}
//ElectricalFacade1
package com.std.www.designPattern.facade;

public class ElectricalFacade1 implements Facade{
    private Computer computer;
    private Fan fan;
    private Hvac hvac;
    private Lamp lamp;
    private TV tv;

    public ElectricalFacade1(Computer computer, Fan fan, Hvac hvac, Lamp lamp, TV tv) {
        this.computer = computer;
        this.fan = fan;
        this.hvac = hvac;
        this.lamp = lamp;
        this.tv = tv;
    }

    @Override
    public void start() {
        lamp.start();
        fan.start();
        hvac.start();
        tv.start();
        computer.start();

    }
    public void play(){
        tv.play();
        computer.play();

    }
    public void pause(){
        tv.pause();
        computer.pause();
    }

    @Override
    public void close() {
        lamp.close();
        fan.close();
        hvac.close();
        tv.close();
        computer.close();
    }
}

//ElectricalFacade2
package com.std.www.designPattern.facade;

public class ElectricalFacade2 implements Facade{
    private Fan fan;
    private Hvac hvac;
    private Lamp lamp;

    public ElectricalFacade2( Fan fan, Hvac hvac, Lamp lamp) {
        this.fan = fan;
        this.hvac = hvac;
        this.lamp = lamp;
    }

    @Override
    public void start() {
        lamp.start();
        fan.start();
        hvac.start();

    }

    @Override
    public void close() {
        lamp.close();
        fan.close();
        hvac.close();
    }

    @Override
    public void play() {

    }

    @Override
    public void pause() {

    }
}


//Client
package com.std.www.designPattern.facade;

public class Client {
    public static void main(String[] args) {
        Facade facade1=new ElectricalFacade1(new Computer(),new Fan(),new Hvac(),new Lamp(),new TV());
        Facade facade2=new ElectricalFacade2(new Fan(),new Hvac(),new Lamp());
        facade1.start();
        facade1.pause();
        facade1.play();
        facade1.close();
        System.out.println("=========================================================");
        facade2.start();
        facade2.close();
    }
}

外观模式一般包含一个抽象外观类,多个子系统角色,外观模式并没有增加新的功能,对于客户端来说如果想要调用这写子系统的功能必须逐个实例化调用,而外观模式新增加了一个外观类,用于充当客户端和子系统的之间的类,相当于一个总系统,客户端只需调用该总系统就可以掉用各个子系统,而不用逐个调用子系统,这样客户和子系统就隔离了,如果新增了一个子系统只需要再新增一个具体外观类即可

主要角色:

  1. 外观(Facade): 提供了一个高层次的接口,该接口使用了子系统中一组接口,它负责将客户端的请求转发给适当的子系统对象。

  2. 子系统(Subsystem): 包含多个模块或类,每个模块都实现了子系统的一部分功能,但是客户端通常不直接与子系统的模块交互,而是通过外观进行操作。

特点:

  • 外观模式定义了一个高层次的接口,使得客户端更容易使用子系统。

  • 外观模式通过将客户端和子系统解耦,减少了客户端与子系统之间的直接依赖关系。

优点:

  • 简化了客户端和子系统之间的交互,客户端不需要了解子系统的内部结构,只需要通过外观进行操作。

  • 降低了客户端与子系统之间的耦合,当子系统发生变化时,客户端的影响较小。

  • 提高了系统的灵活性,客户端可以通过外观访问子系统的功能,而不受子系统内部变化的影响。

缺点:

  • 可能会导致外观对象变得庞大而且臃肿,因为它需要知道子系统中所有的细节。

适用场景:

  • 当需要简化一个复杂系统的接口,提供一个更高层次的接口给客户端时,可以使用外观模式。

  • 当希望将客户端和子系统解耦,减少它们之间的依赖关系时,可以使用外观模式。

  • 当子系统的组件很多,而且它们之间的通信复杂时,可以使用外观模式。

享元模式

//Automobile
package com.std.www.designPattern.flyweight;

public interface Automobile {
    String getType();
    void use(Passenger passenger);
}

//Bus
package com.std.www.designPattern.flyweight;

public class Bus implements Automobile{
    private String type;

    public Bus(String type) {
        this.type = type;
    }

    @Override
    public String getType() {
        return this.type;
    }

    @Override
    public void use(Passenger passenger) {
        System.out.println("车辆类型为:"+type+" 乘客姓名为:"+passenger.getName());
    }
}
//Car
package com.std.www.designPattern.flyweight;

public class Car implements Automobile{
    private String type;

    public Car(String type) {
        this.type = type;
    }

    @Override
    public String getType() {
        return this.type;
    }

    @Override
    public void use(Passenger passenger) {
        System.out.println("车辆类型为:"+type+" 乘客姓名为:"+passenger.getName());
    }
}

//Passenger
package com.std.www.designPattern.flyweight;

public class Passenger {
    private String name;

    public Passenger(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

//AutomobileFactory
package com.std.www.designPattern.flyweight;

import java.util.HashMap;

public class AutomobileFactory {
    private HashMap<String,Automobile> automobileHashMap=new HashMap<>();
    public Automobile getAutomobile(String key,String type){
        if(!automobileHashMap.containsKey(key)){
            Automobile automobile=null;
            if(type.equals("bus")){
                automobile=new Bus(key);
            }
            else if(type.equals("car")){
                automobile=new Car(key);
            }
            if(automobile!=null) automobileHashMap.put(key,automobile);
        }
        return automobileHashMap.get(key);
    }
    public int count(){
        return automobileHashMap.size();
    }
}

//Client
package com.std.www.designPattern.flyweight;

public class Client {
    public static void main(String[] args) {
        AutomobileFactory factory=new AutomobileFactory();
       Automobile automobile1=factory.getAutomobile("大型公交车","bus");
       automobile1.use(new Passenger("张三"));
       Automobile automobile2=factory.getAutomobile("小型公交车","bus");
       automobile2.use(new Passenger("张四"));
       Automobile automobile3=factory.getAutomobile("大型公交车","bus");
       automobile3.use(new Passenger("张五"));
       Automobile automobile4=factory.getAutomobile("中型轿车","car");
       automobile4.use(new Passenger("王明"));
       System.out.println("车辆数量:"+factory.count());
    }
}

享元模式一般含有一个抽象享元类Flyweight,其子类有共享实习类ConcreteFlyweight和非共享实现类UnsharedConcreteFlyweight,还有一个享元工厂类,享元类对象属性分为外部状态和内部状态,其中内部状态一般是稳定可以共享的,例如棋盘中有黑子和白子,其中棋子的黑白就是内部状态,而棋子的位置就是外部状态,享元模式最大的应用就是池技术,像线程池,数据库连接池等都是享元模式,享元模式本质就是对象共享,像上面的例子中,一般来说我们给每个乘客都需要创建一个机动车对象,但是实际上一辆机动车可以乘坐多名乘客,即乘客共享一辆车,这里机动车类型为内部状态,乘客姓名为外部状态,享元工厂一般采用简单工厂模式,每个共享对象都有一个标签,如果池子里面有就无需新建对象,减少了内存消耗

主要角色:

  1. 享元工厂(Flyweight Factory): 负责创建和管理享元对象。它通常维护一个享元池,存储已经创建的享元对象,并在需要时返回它们。

  2. 具体享元(Concrete Flyweight): 实现享元接口,并为内部状态提供存储。具体享元对象需要是可共享的,即多个客户端可同时引用它。

  3. 非共享具体享元(Unshared Concrete Flyweight): 与共享享元相似,但是不能被共享。这些对象通常包含了不同的内部状态或是外部状态。

  4. 客户端(Client): 使用享元对象的地方,维护对享元的引用。

特点:

  • 享元模式通过共享内部状态来减小对象数量,从而减小内存占用和提高性能。

  • 内部状态是对象可共享的部分,而外部状态是对象不可共享的部分。

优点:

  • 减小内存占用,提高性能,特别是在需要大量相似对象的情况下。

  • 外部状态相对较少的情况下,可以大幅减少创建对象的数量。

缺点:

  • 需要注意对外部状态的管理,因为外部状态不能被共享,每个对象需要管理自己的外部状态。

  • 可能引入系统的复杂性,需要维护内部状态和外部状态之间的关系。

适用场景:

  • 当一个类创建的实例数量非常多,且对象的大部分状态都可以共享时,可以考虑使用享元模式。

  • 当对象的大部分状态都可以抽离出来并存储在外部时,可以使用享元模式。这样内部状态可以共享,而外部状态可以由客户端来管理。

 

代理模式

静态代理

//Recruit
package com.std.www.designPattern.proxy;

public interface Recruit {
    void recruit();
}

//Firm
package com.std.www.designPattern.proxy;

public class Firm implements Recruit{
    @Override
    public void recruit() {
        System.out.println("公司招聘员工");
    }
}

//Proxy
package com.std.www.designPattern.proxy;

public class Proxy implements Recruit{
    private Recruit firm;

    public Proxy(Recruit firm) {
        this.firm = firm;
    }

    @Override
    public void recruit() {
        findJob();
        toll();
        signContract();
        firm.recruit();
    }
    public void findJob(){
        System.out.println("找寻工作");
    }
    public void toll(){
        System.out.println("收取中介费");
    }
    public void signContract(){
        System.out.println("签订劳工合同");
    }
}

//Client
package com.std.www.designPattern.proxy;

public class Client {
    public static void main(String[] args) {
        Recruit firm=new Firm();
        Proxy proxy=new Proxy(firm);
        proxy.recruit();
    }
}

静态代理一般包含一个抽象主题角色,一个代理主题角色和多个真实主题角色,代理模式的好处就是AOP思想,即切面编程,把真实角色和客户端隔离,通过代理类在完成真实角色的任务之外还可以添加额外的功能,代理角色可以代理多个相同主题的真实角色

动态代理

 

//Recruit
package com.std.www.designPattern.proxy;

public interface Recruit {
    void recruit();
}

//Firm
package com.std.www.designPattern.proxy;

public class Firm implements Recruit{
    @Override
    public void recruit() {
        System.out.println("公司招聘员工");
    }
}

//DynamicProxy
package com.std.www.designPattern.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class DynamicProxy implements InvocationHandler {
    private Recruit recruit;

    public void setRecruit(Recruit recruit) {
        this.recruit = recruit;
    }

    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), recruit.getClass().getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        findJob();
        toll();
        signContract();
        Object res = method.invoke(recruit, args);
        return res;
    }
    public void findJob(){
        System.out.println("找寻工作");
    }
    public void toll(){
        System.out.println("收取中介费");
    }
    public void signContract(){
        System.out.println("签订劳工合同");
    }
}


//Client
package com.std.www.designPattern.proxy;

public class Client {
    public static void main(String[] args) {
        Firm firm=new Firm();
        DynamicProxy dynamicProxy=new DynamicProxy();
        dynamicProxy.setRecruit(firm);
        Recruit proxy= (Recruit) dynamicProxy.getProxy();
        proxy.recruit();
    }
}

动态代理相比于静态代理,利用反射的机制,动态生成代理对象而无需编写具体的代理减少了代码量

主要角色:

  1. 抽象主题(Subject): 定义了目标对象和代理对象的共同接口,这样在任何使用目标对象的地方都可以使用代理对象。

  2. 真实主题(Real Subject): 定义了代理所代表的真实对象,是代理模式中的目标对象。

  3. 代理(Proxy): 包含了对真实主题的引用,并提供与真实主题相同的接口,以便在任何时候都能替代真实主题。

  4. 客户端(Client): 使用代理对象的地方,通过代理对象间接访问真实对象。

特点:

  • 代理模式可以控制对真实对象的访问,可以在访问前后添加一些额外的逻辑。

  • 代理对象可以在不改变真实对象的情况下扩展其功能,如懒加载、缓存等。

优点:

  • 代理模式能够提高系统的灵活性和可扩展性,因为客户端无需知道真实对象,只需通过代理对象访问即可。

  • 代理对象可以在不修改目标对象的情况下,增加额外的功能,例如权限控制、日志记录等。

缺点:

  • 增加了系统的复杂性,因为引入了代理对象,可能需要额外的代码。

  • 由于代理模式引入了代理对象,可能会造成请求的处理速度变慢。

适用场景:

  • 远程代理:通过代理实现对象在不同地址空间的访问。

  • 虚拟代理:通过代理实现对资源的延迟加载。

  • 保护代理:通过代理控制对对象的访问权限。

  • 缓存代理:通过代理实现对结果的缓存。

结构型模式就7个

适配器模式,桥接模式,组合模式,装饰模式,外观模式,享元模式,代理模式

posted @ 2023-10-07 23:20  突破铁皮  阅读(1)  评论(0编辑  收藏  举报