「Java」设计模式-结构型

这是我参与11月更文挑战的第14天,活动详情查看:2021最后一次更文挑战

适配器模式

将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

image.png

关键代码

适配器继承或依赖已有的对象,实现想要的目标接口。

优点

  1. 可以让任何两个没有关联的类一起运行。

  2. 提高了类的复用。

  3. 增加了类的透明度。

  4. 灵活性好。

缺点

  1. 过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。

  2. 由于 JAVA 至多继承一个类,所以至多只能适配一个适配者类,而且目标类必须是抽象类。

实现

我们有一个 MediaPlayer 接口和一个实现了 MediaPlayer 接口的实体类 AudioPlayer。默认情况下,AudioPlayer 可以播放 mp3 格式的音频文件。我们还有另一个口 AdvancedMediaPlayer 和实现了 AdvancedMediaPlayer 接口的实体类。该类可以播放 vlc 和 mp4 格式的文件。我们想要让 AudioPlayer 播放其他格式的音频文件。为了实现这个功能,我们需要创建一个实现了 MediaPlayer 接口的适配器类 MediaAdapter,并使用 AdvancedMediaPlayer 对象来播放所需的格式。

为媒体播放器和更高级的媒体播放器创建接口。


public interface MediaPlayer {

    public void play(String audioType, String fileName);

}

复制代码

public interface AdvancedMediaPlayer {

    public void playVlc(String fileName);

    public void playMp4(String fileName);

}

复制代码

创建实现了 AdvancedMediaPlayer 接口的实体类。


public class VlcPlayer implements AdvancedMediaPlayer{

    @Override
    public void playVlc(String fileName) {
        System.out.println("Playing vlc file. Name: "+ fileName);
    }

    @Override
    public void playMp4(String fileName) {
        //什么也不做
    }

}

复制代码

public class Mp4Player implements AdvancedMediaPlayer{

    @Override
    public void playVlc(String fileName) {
        //什么也不做
    }

    @Override
    public void playMp4(String fileName) {
        System.out.println("Playing mp4 file. Name: "+ fileName);
    }

}

复制代码

创建实现了 MediaPlayer 接口的适配器类。


public class MediaAdapter implements MediaPlayer {

    AdvancedMediaPlayer advancedMusicPlayer;

    public MediaAdapter(String audioType){

        if(audioType.equalsIgnoreCase("vlc") ){
            advancedMusicPlayer = new VlcPlayer();
        } else if (audioType.equalsIgnoreCase("mp4")){
            advancedMusicPlayer = new Mp4Player();
        }
    }

    @Override
    public void play(String audioType, String fileName) {

        if(audioType.equalsIgnoreCase("vlc")){

            advancedMusicPlayer.playVlc(fileName);

        }else if(audioType.equalsIgnoreCase("mp4")){

            advancedMusicPlayer.playMp4(fileName);

        }

    }

}

复制代码

创建实现了 MediaPlayer 接口的实体类。


public class AudioPlayer implements MediaPlayer {

    MediaAdapter mediaAdapter;

    @Override
    public void play(String audioType, String fileName) {

        //播放 mp3 音乐文件的内置支持
        if(audioType.equalsIgnoreCase("mp3")){
            System.out.println("Playing mp3 file. Name: "+ fileName);
        } else if(audioType.equalsIgnoreCase("vlc") || audioType.equalsIgnoreCase("mp4")){
            //mediaAdapter 提供了播放其他文件格式的支持
            mediaAdapter = new MediaAdapter(audioType);
            mediaAdapter.play(audioType, fileName);
        } else{
            System.out.println("Invalid media. "+
            audioType + " format not supported");
        }
    }
}

复制代码

使用 AudioPlayer 来播放不同类型的音频格式。


public class AdapterPatternDemo {

    public static void main(String[] args) {

        AudioPlayer audioPlayer = new AudioPlayer();

        audioPlayer.play("mp3", "beyond the horizon.mp3");
        audioPlayer.play("mp4", "alone.mp4");
        audioPlayer.play("vlc", "far far away.vlc");
        audioPlayer.play("avi", "mind me.avi");

    }

}

复制代码
组合模式

将对象组合成树形结构以表示"部分-整体"的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

关键代码

树枝内部组合该接口,并且含有内部属性 List,里面放 Component。

优点

  1. 高层模块调用简单。

  2. 节点自由增加。

缺点

在使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则。

实现

有一个类 Employee,该类被当作组合模型类。CompositePatternDemo,演示类使用 Employee 类来添加部门层次结构,并打印所有员工。

image.png

创建 Employee 类,该类带有 Employee 对象的列表。


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

public class Employee {

    private String name;

    private String dept;

    private int salary;

    private List<Employee> subordinates;

    //构造函数

    public Employee(String name,String dept, int sal) {
        this.name = name;
        this.dept = dept;
        this.salary = sal;
        subordinates = new ArrayList<Employee>();
    }

    public void add(Employee e) {
        subordinates.add(e);
    }

    public void remove(Employee e) {
        subordinates.remove(e);
    }

    public List<Employee> getSubordinates(){
        return subordinates;
    }

    public String toString(){
        return ("Employee :[ Name : "+ name
        +", dept : "+ dept + ", salary :"
        + salary+" ]");
    }

}

复制代码

使用 Employee 类来创建和打印员工的层次结构。


public class CompositePatternDemo {

    public static void main(String[] args) {

        Employee CEO = new Employee("John","CEO", 30000);
        Employee headSales = new Employee("Robert","Head Sales", 20000);
        Employee headMarketing = new Employee("Michel","Head Marketing", 20000);
        Employee clerk1 = new Employee("Laura","Marketing", 10000);
        Employee clerk2 = new Employee("Bob","Marketing", 10000);
        Employee salesExecutive1 = new Employee("Richard","Sales", 10000);
        Employee salesExecutive2 = new Employee("Rob","Sales", 10000);

        CEO.add(headSales);
        CEO.add(headMarketing);

        headSales.add(salesExecutive1);
        headSales.add(salesExecutive2);

        headMarketing.add(clerk1);
        headMarketing.add(clerk2);

        //打印该组织的所有员工

        System.out.println(CEO);

        for (Employee headEmployee : CEO.getSubordinates()) {
            System.out.println(headEmployee);
                for (Employee employee : headEmployee.getSubordinates()) {
                    System.out.println(employee);
                }
        }
    }
}

复制代码
装饰模式

动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。

关键代码

  1. Component 类充当抽象角色,不应该具体实现。

  2. 修饰类引用和继承 Component 类,具体扩展类重写父类方法。

优点

装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。

缺点

多层装饰比较复杂。

实现

不论一幅画有没有画框都可以挂在墙上,但是通常都是有画框的,并且实际上是画框被挂在墙上。在挂在墙上之前,画可以被蒙上玻璃,装到框子里;这时画、玻璃和画框形成了一个物体。

创建一个 Shape 接口和实现了 Shape 接口的实体类。然后创建一个实现了 Shape 接口的抽象装饰类 ShapeDecorator,并把 Shape对象作为它的实例变量。RedShapeDecorator 是实现了 ShapeDecorator 的实体类。DecoratorPatternDemo,我们的演示类使用 RedShapeDecorator 来装饰 Shape 对象。

image.png

创建一个接口:


public interface Shape {
    void draw();
}

复制代码

创建实现接口的实体类。


public class Rectangle implements Shape {

    @Override
    public void draw() {
        System.out.println("Shape: Rectangle");
    }

}

复制代码

public class Circle implements Shape {

    @Override
    public void draw() {
        System.out.println("Shape: Circle");
    }

}

复制代码

创建实现了 Shape 接口的抽象装饰类。


public abstract class ShapeDecorator implements Shape {

    protected Shape decoratedShape;

    public ShapeDecorator(Shape decoratedShape){
        this.decoratedShape = decoratedShape;
    }

    public void draw(){
        decoratedShape.draw();
    }

}

复制代码

创建扩展了 ShapeDecorator 类的实体装饰类。


public class RedShapeDecorator extends ShapeDecorator {

    public RedShapeDecorator(Shape decoratedShape) {
        super(decoratedShape);
    }

    @Override
    public void draw() {
        decoratedShape.draw();
        setRedBorder(decoratedShape);
    }

    private void setRedBorder(Shape decoratedShape){
        System.out.println("Border Color: Red");
    }

}

复制代码

使用 RedShapeDecorator 来装饰 Shape 对象。


public class DecoratorPatternDemo {

    public static void main(String[] args) {

        Shape circle = new Circle();
        ShapeDecorator redCircle = new RedShapeDecorator(new Circle());
        ShapeDecorator redRectangle = new RedShapeDecorator(new Rectangle());
        //Shape redCircle = new RedShapeDecorator(new Circle());
        //Shape redRectangle = new RedShapeDecorator(new Rectangle());

        System.out.println("Circle with normal border");
        circle.draw();
        System.out.println("\nCircle of red border");
        redCircle.draw();
        System.out.println("\nRectangle of red border");
        redRectangle.draw();
    
    }

}

复制代码
代理模式

为其他对象提供一种代理以控制对这个对象的访问。

关键代码

实现与被代理类组合

优点

  1. 职责清晰。

  2. 高扩展性。

  3. 智能化。

缺点

  1. 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。

  2. 实现代理模式需要额外的工作,有些代理模式的实现非常复杂。

实现

Windows 里面的快捷方式。

创建一个 Image 接口和实现了 Image 接口的实体类。ProxyImage 是一个代理类,减少 RealImage 对象加载的内存占用。ProxyPatternDemo,我们的演示类使用 ProxyImage 来获取要加载的 Image 对象,并按照需求进行显示

image.png

创建一个接口。


public interface Image {
    void display();
}

复制代码

创建实现接口的实体类。


public class RealImage implements Image {

    private String fileName;

    public RealImage(String fileName){
        this.fileName = fileName;
        loadFromDisk(fileName);
    }

    @Override
    public void display() {
        System.out.println("Displaying " + fileName);
    }

    private void loadFromDisk(String fileName){
        System.out.println("Loading " + fileName);
    }

}

复制代码

public class ProxyImage implements Image{

    private RealImage realImage;

    private String fileName;

    public ProxyImage(String fileName){
        this.fileName = fileName;
    }

    @Override
    public void display() {
        if(realImage == null){
            realImage = new RealImage(fileName);
        }
        realImage.display();

    }

}

复制代码

当被请求时,使用 ProxyImage 来获取 RealImage 类的对象。


public class ProxyPatternDemo {

    public static void main(String[] args) {

        Image image = new ProxyImage("test_10mb.jpg");
        // 图像将从磁盘加载
        image.display();
        System.out.println("");
        // 图像不需要从磁盘加载
        image.display();
    }

}

复制代码
亨元(蝇量)模式

运用共享技术有效地支持大量细粒度的对象

关键代码

用 HashMap 存储这些对象。

优点:大大减少对象的创建,降低系统的内存,使效率提高。

缺点:提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱。

实现

创建一个 Shape 接口和实现了 Shape 接口的实体类 Circle。下一步是定义工厂类 ShapeFactory。ShapeFactory 有一个 Circle 的 HashMap,其中键名为 Circle 对象的颜色。无论何时接收到请求,都会创建一个特定颜色的圆。ShapeFactory 检查它的 HashMap 中的 circle 对象,如果找到 Circle 对象,则返回该对象,否则将创建一个存储在 hashmap 中以备后续使用的新对象,并把该对象返回到客户端。FlyWeightPatternDemo,我们的演示类使用 ShapeFactory 来获取 Shape 对象。它将向 ShapeFactory 传递信息(red / green / blue/ black / white),以便获取它所需对象的颜色。

image.png

创建一个接口。


public interface Shape {
    void draw();
}

复制代码

创建实现接口的实体类。


public class Circle implements Shape {

    private String color;

    private int x;

    private int y;

    private int radius;

    public Circle(String color){
        this.color = color;
    }

    public void setX(int x) {
        this.x = x;
    }

    public void setY(int y) {
        this.y = y;
    }

    public void setRadius(int radius) {
        this.radius = radius;
    }

    @Override
    public void draw() {
        System.out.println("Circle: Draw() [Color : " + color
        +", x : " + x +", y :" + y +", radius :" + radius);
    }

}

复制代码

创建一个工厂,生成基于给定信息的实体类的对象


import java.util.HashMap;

public class ShapeFactory {

    private static final HashMap<String, Shape> circleMap = new HashMap<>();

    public static Shape getCircle(String color) {

        Circle circle = (Circle)circleMap.get(color);

        if(circle == null) {
            circle = new Circle(color);
            circleMap.put(color, circle);
            System.out.println("Creating circle of color : " + color);
        }

        return circle;

    }

}

复制代码

使用该工厂,通过传递颜色信息来获取实体类的对象。


public class FlyweightPatternDemo {

    private static final String colors[] =

    { "Red", "Green", "Blue", "White", "Black" };

    public static void main(String[] args) {

        for(int i=0; i < 20; ++i) {
            Circle circle = (Circle)ShapeFactory.getCircle(getRandomColor());
            circle.setX(getRandomX());
            circle.setY(getRandomY());
            circle.setRadius(100);
            circle.draw();
        }

    }

    private static String getRandomColor() {
        return colors[(int)(Math.random()*colors.length)];
    }

    private static int getRandomX() {
        return (int)(Math.random()*100 );
    }

    private static int getRandomY() {
        return (int)(Math.random()*100);
    }

}

复制代码
外观模式

为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

关键代码

在客户端和复杂系统之间再加一层,这一层将调用顺序、依赖关系等处理好

优点

  1. 减少系统相互依赖。

  2. 提高灵活性。

3.提高了安全性。

缺点

不符合开闭原则,如果要改东西很麻烦,继承重写都不合适。

实现

创建一个 Shape 接口和实现了 Shape 接口的实体类。下一步是定义一个外观类 ShapeMaker。ShapeMaker 类使用实体类来代表用户对这些类的调用。FacadePatternDemo,我们的演示类使用 ShapeMaker 类来显示结果

image.png 创建一个接口。


public interface Shape {
    void draw();
}

复制代码

创建实现接口的实体类。


public class Rectangle implements Shape {

    @Override
    public void draw() {
        System.out.println("Rectangle::draw()");
    }

}

复制代码

public class Square implements Shape {

    @Override
    public void draw() {
        System.out.println("Square::draw()");
    }

}

复制代码

public class Circle implements Shape {

    @Override
    public void draw() {
        System.out.println("Circle::draw()");
    }

}

复制代码

创建一个外观类。


public class ShapeMaker {

    private Shape circle;

    private Shape rectangle;

    private Shape square;

    public ShapeMaker() {
        circle = new Circle();
        rectangle = new Rectangle();
        square = new Square();
    }

    public void drawCircle(){
        circle.draw();
    }

    public void drawRectangle(){
        rectangle.draw();
    }

    public void drawSquare(){
        square.draw();
    }

}

复制代码

使用该外观类画出各种类型的形状。


public class FacadePatternDemo {

    public static void main(String[] args) {

        ShapeMaker shapeMaker = new ShapeMaker();
        shapeMaker.drawCircle();
        shapeMaker.drawRectangle();
        shapeMaker.drawSquare();
    }

}

复制代码
桥接模式

将抽象部分与实现部分分离,使它们都可以独立的变化。

关键代码

抽象类依赖实现类。

优点

  1. 抽象和实现的分离。

  2. 优秀的扩展能力。

  3. 实现细节对客户透明。

缺点

桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。

实现

有一个作为桥接实现的 DrawAPI 接口和实现了 DrawAPI 接口的实体类 RedCircle、GreenCircle。Shape 是一个抽象类,将使用 DrawAPI 的对象。BridgePatternDemo,演示类使用 Shape 类来画出不同颜色的圆。

image.png

创建桥接实现接口。


public interface DrawAPI {
    public void drawCircle(int radius, int x, int y);
}

复制代码

创建实现了 DrawAPI 接口的实体桥接实现类。


public class RedCircle implements DrawAPI {

    @Override
    public void drawCircle(int radius, int x, int y) {
        System.out.println("Drawing Circle[ color: red, radius: "
        + radius +", x: " +x+", "+ y +"]");
    }

}

复制代码

public class GreenCircle implements DrawAPI {

    @Override
    public void drawCircle(int radius, int x, int y) {
        System.out.println("Drawing Circle[ color: green, radius: "
        + radius +", x: " +x+", "+ y +"]");
    }

}

复制代码

使用 DrawAPI 接口创建抽象类 Shape。


public abstract class Shape {

    protected DrawAPI drawAPI;

    protected Shape(DrawAPI drawAPI){
        this.drawAPI = drawAPI;
    }

    public abstract void draw();

}

复制代码

创建实现了 Shape 接口的实体类。


public class Circle extends Shape {

    private int x, y, radius;

    public Circle(int x, int y, int radius, DrawAPI drawAPI) {
        super(drawAPI);

        this.x = x;
        this.y = y;
        this.radius = radius;
    }

    public void draw() {
        drawAPI.drawCircle(radius,x,y);
    }

}

复制代码

使用 Shape 和 DrawAPI 类画出不同颜色的圆。


public class BridgePatternDemo {

    public static void main(String[] args) {

        Shape redCircle = new Circle(100,100, 10, new RedCircle());
        Shape greenCircle = new Circle(100,100, 10, new GreenCircle());

        redCircle.draw();
        greenCircle.draw();

    }

}

复制代码

猜你喜欢

转载自juejin.im/post/7031097333764849678