ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

大话设计模式(根据狂神说视频,但不全)

2021-05-13 16:03:59  阅读:118  来源: 互联网

标签:void System public Override println 狂神 设计模式 大话 out


大话设计模式(根据狂神说视频,但不全)

一、概述

​ 设计模式是前辈对特定问题的解决办法。是面向对象设计原则的实际运用,针对封装、继承和多态。

设计模式优点:

​ 提升编程能力、提升程序标准化、提升程序效率、可重用性可读性等更高。

设计模式基本要素

​ 模式名称、问题、解决方案、效果。

范围\目的创建型模式结构型模式行为型模式
类模式工厂方法(类)适配器模板方法、解释器
对象模式单例 原型 抽象工厂 建造者代理 (对象)适配器 桥接 装饰 外观 享元 组合策略 命令 职责链 状态 观察者 中介者 迭代器 访问者 备忘录

OOP七大原则

​ **开闭原则: ** 对扩展开放, 对修改关闭
​ 里氏替换原则: 继承必须确保超类所拥有的性质在子类中仍然成立
​ **依赖倒置原则: ** 要面向接口编程, 不要面向实现编程
​ **单一职责原则: ** 控制类的粒度大小, 将对象解耦, 提高其内聚性
​ 接口隔离原则: 要为各个类建立它们需要的专用接口
​ **迪米特法则: **只与你的直接朋友交谈,不跟"陌生人"说话
​ **合成复用原则: **尽量先使用组合或者聚合等关联关系实现,其次才考虑使用继承关系来实现

二、工厂模式

​ 作用:实现了调用者和生产者分离。

​ 分类:简单工厂模式、工厂方法模式、抽象工厂模式。

1、简单工厂模式

​ JavaSE阶段我们使用new对象的方式,但是需要知道子类型的对象,而且参数要我们自己传。简单工厂模式,我们只需要给工厂传递我们需要哪种车即可。

​ 写车接口、两个实现类以及顾客模拟原始方式。

public interface Car {
    void name();
}

public class Benz implements Car{
    @Override
    public void name() {
        System.out.println("奔驰...");
    }
}

public class Porsche implements Car{
    @Override
    public void name() {
        System.out.println("保时捷...");
    }
}

public class Customer {
    public static void main(String[] args) {
        //以前的方式要了解接口和实现类
        Car car1 = new Benz();
        Car car2 = new Porsche();
        car1.name();
        car2.name();
    }
}

​ 模拟简单工厂的方式。创建工厂,顾客买车。

public class CarFactory {
    public static Car getCar(String car){
        if (car.equals("奔驰")){
            return new Benz();
        }else if (car.equals("保时捷")){
            return new Porsche();
        }
        return null;
    }
}
public class Customer {
    public static void main(String[] args) {
        //使用工厂类
        Car car1 = CarFactory.getCar("保时捷");
        Car car2 = CarFactory.getCar("奔驰");
        car1.name();
        car2.name();
    }
}

2、工厂方法模式

​ 简单工厂方式虽然实现了解耦创建者和调用者,但是有个问题,出新车的时候,对工厂类还是要进行修改,违背了开闭原则。

​ 所以采用工厂方法模式,相当于又加了一层。本来从工厂创建一个对象,但是如果加新车工厂就要改代码。所以不如再加一层,加一个总工厂,把各个车厂单独变成一个类,那么加新车的话,就等于直接创建一个车的工厂,从工厂里拿车。这样就满足开闭原则,扩展开放,修改关闭。

​ 那么加一个工厂接口,其他工厂变成其子工厂。

public interface CarFactory {
    Car getCar();
}

public class BenzFactory implements CarFactory{
    @Override
    public Car getCar() {
        return new Benz();
    }
}

public class PorscheFactory implements CarFactory{
    @Override
    public Car getCar() {
        return new Porsche();
    }
}

​ 调用。

public class Customer {
    public static void main(String[] args) {
        Car car1 = new PorscheFactory().getCar();
        Car car2 = new BenzFactory().getCar();
        car1.name();
        car2.name();
    }
}

​ 现在加新车和车厂。

public class MoBai implements Car{
    @Override
    public void name() {
        System.out.println("摩拜...");
    }
}

public class MobaiFactory implements CarFactory{
    @Override
    public Car getCar() {
        return new MoBai();
    }
}

​ 这样没有修改源码,直接扩展即可用。

3、抽象工厂模式

​ 定义:抽象工厂模式添加了创建一系列相关或者有依赖关系的对象的接口,无需指定他们的具体类。

image-20210218170751011

​ 这样横向为同一个产品,纵向为同一个类型。然后给它们分别抽象,然后再度抽象成一个笼统的概念。

优缺点:

​ 优点:

​ 1、具体产品在应用层的代码隔离,无需关心创建的细节;
​ 2、将一个系列的产品统一到一起创建;
​ 缺点:
​ 1、规定了所有可能被创建的产品集合,产品簇中扩展新的产品困难;
​ 2、增加了系统的抽象性和理解难度;

​ 代码如下。

​ 先定义手机、路由的接口。

public interface RouterProduct {
    void start();
    void shutdown();
    void openwifi();
    void setting();
}

public interface IphoneProduct {
    void start();
    void shutdown();
    void call();
    void sendMessage();
}

​ 然后定义不同产品的路由和手机类,分别实现接口。

public class HuaweiRouter implements RouterProduct{
    @Override
    public void start() {
        System.out.println("启动华为路由器");
    }

    @Override
    public void shutdown() {
        System.out.println("关闭华为路由器");
    }

    @Override
    public void openwifi() {
        System.out.println("打开华为的wifi");
    }

    @Override
    public void setting() {
        System.out.println("华为路由器设置");
    }
}

public class XiaomiRouter implements RouterProduct{
    @Override
    public void start() {
        System.out.println("启动小米路由器");
    }

    @Override
    public void shutdown() {
        System.out.println("关闭小米路由器");
    }

    @Override
    public void openwifi() {
        System.out.println("打开小米的wifi");
    }

    @Override
    public void setting() {
        System.out.println("小米路由器设置");
    }
}
public class HuaweiPhone implements IphoneProduct{
    @Override
    public void start() {
        System.out.println("开启华为手机");
    }

    @Override
    public void shutdown() {
        System.out.println("关闭华为手机");
    }

    @Override
    public void call() {
        System.out.println("华为手机打电话");
    }

    @Override
    public void sendMessage() {
        System.out.println("华为手机发短信");
    }
}

public class XiaomiPhone implements IphoneProduct{
    @Override
    public void start() {
        System.out.println("开启小米手机");
    }

    @Override
    public void shutdown() {
        System.out.println("关闭小米手机");
    }

    @Override
    public void call() {
        System.out.println("小米手机打电话");
    }

    @Override
    public void sendMessage() {
        System.out.println("小米手机发短信");
    }
}

​ 这样横向和纵向的这四个产品都定义好了,然后定义一个抽象接口的抽象工厂。

public interface ProductFactory {
    //生产手机
    IphoneProduct iphoneProduct();

    //生产路由器
    RouterProduct routerProduct();
}

​ 这样就等同于都关联起来了。然后测试即可。

public class Client {
    public static void main(String[] args) {
        System.out.println("================小米系列产品=================");
        XiaomiFactory xiaomiFactory = new XiaomiFactory();
        IphoneProduct xiaomiPhone = xiaomiFactory.iphoneProduct();
        RouterProduct xiaomiRouter = xiaomiFactory.routerProduct();
        xiaomiPhone.call();
        xiaomiRouter.openwifi();

        System.out.println("================华为系列产品=================");
        HuaweiFactory huaweiFactory = new HuaweiFactory();
        IphoneProduct huaweiPhone = huaweiFactory.iphoneProduct();
        RouterProduct huaweiRouter = huaweiFactory.routerProduct();
        huaweiPhone.call();
        huaweiRouter.openwifi();
    }
}

​ 其实就是这个样子的结构。

image-20210513114943343

​ 但是扩展起来会非常麻烦,会强行要求实现很多代码。这还只是纵向扩展,如果扩展品牌还是很麻烦,但是如果修改ProductFactory之后,基本不会修改了,这种方式还是可取的,因为它关联性、可读性很强。

image-20210513115454593

二、建造者模式

​ 建造者模式为创建对象提供了一种最佳的方式。也就是将一个对象的创建方式封装起来,直接使用创建方法即可。

​ 这样用户就可以在不知道创建细节的情况下,创建对象了。

​ 下面看代码。

​ 比如盖一个楼需要工人,需要一个工头作为指挥,而我们只需要让工头去干即可,而不需要在意创建的细节。

​ 先创建一个楼的产品类。

public class Product {
    private String buildA;
    private String buildB;
    private String buildC;
    private String buildD;

    public String getBuildA() {
        return buildA;
    }

    public void setBuildA(String buildA) {
        this.buildA = buildA;
    }

    public String getBuildB() {
        return buildB;
    }

    public void setBuildB(String buildB) {
        this.buildB = buildB;
    }

    public String getBuildC() {
        return buildC;
    }

    public void setBuildC(String buildC) {
        this.buildC = buildC;
    }

    public String getBuildD() {
        return buildD;
    }

    public void setBuildD(String buildD) {
        this.buildD = buildD;
    }

    @Override
    public String toString() {
        return "Product{" +
                "buildA='" + buildA + '\'' +
                ", buildB='" + buildB + '\'' +
                ", buildC='" + buildC + '\'' +
                ", buildD='" + buildD + '\'' +
                '}';
    }
}

​ 因为可能有多个工人,所以先使用抽象类创建。

public abstract class Builder {
    abstract void buildA();
    abstract void buildB();
    abstract void buildC();
    abstract void buildD();
    abstract Product getProduct();
}

​ 工人继承。

public class Worker  extends Builder{
    private Product product;

    public Worker (){
        product = new Product();
    }

    @Override
    void buildA() {
        product.setBuildA("地基");
        System.out.println("地基");
    }

    @Override
    void buildB() {
        product.setBuildB("钢筋");
        System.out.println("钢筋");
    }

    @Override
    void buildC() {
        product.setBuildC("混凝土");
        System.out.println("混凝土");
    }

    @Override
    void buildD() {
        product.setBuildD("装修");
        System.out.println("装修");
    }

    @Override
    Product getProduct() {
        return product;
    }
}

​ 创建指挥。

public class Director {
    public Product build(Builder builder){
        builder.buildA();
        builder.buildB();
        builder.buildC();
        builder.buildD();
        return builder.getProduct();
    }
}

​ 最后测试。

public class Customer {
    public static void main(String[] args) {
        Builder builder = new Worker();
        Product product = new Director().build(builder);
        System.out.println(product.toString());
    }
}

​ 这种方式使得我们不需要知道build方法是如何一层一层完成的。我们只需要关注于使用指挥调用它的build方法即可。

优缺点:

​ 优点:
​ 1、产品的建造和表示分离, 实现了解耦, 使用建造者模式可以使客户端不必知道产品内部组成的细节;
​ 2、将复杂产品的创建步骤分解在不同的方法中, 使得创建过程更加清晰;
​ 3、具体的建造者类之间是相互独立的, 这有利于系统的扩展. 增加新的具体建造者无需修改原有的类库的代码, 符合"开闭原则"。
​ 缺点:
​ 1、建造者模式所创建的产品一般具有较多的共同点,其组成部分相似; 如果产品之间的差异性很大, 则不适合使用建造者模式, 因此其使用范围受到一定的限制;
​ 2、如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类实现这种变化, 导致系统变得很庞大;

三、原型模式

​ 一个对象很可能已经封装过了,其中很多方法和数据我们是不能拿到的(如果它不提供一个private参数的入口的话),那么假如我们想拿到这个对象的一模一样的对象怎么办呢?以前的办法是没办法,我们只能创建和当前对象类一模一样的类,但是却拿不到它的私有内部的内容,原型对象就是解决这个问题的。

​ 通过实现Clonable接口,重写clone方法就可以拿到这个对象的复制。

​ 代码如下。给一个视频的类,去实现Cloneable接口。

public class Video implements Cloneable{
    private String name;
    private Date createTime;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    public Video() {
    }

    public Video(String name, Date createTime) {
        this.name = name;
        this.createTime = createTime;
    }

    public String getName() {
        return name;
    }

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

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    @Override
    public String toString() {
        return "Video{" +
                "name='" + name + '\'' +
                ", createTime=" + createTime +
                '}';
    }
}

​ 然后测试。

public class BiliBili {
    public static void main(String[] args) throws CloneNotSupportedException {
        //原型对象
        Date date = new Date();
        Video video1 = new Video("设计模式",date);
        Video video2 = (Video) video1.clone();
        System.out.println("video1=>" + video1);
        System.out.println("video2=>" + video2);
        System.out.println("=====================");

        //这样发现video1和video2都变了说明克隆了它的值和引用
        date.setTime(22222);
        System.out.println("video1=>" + video1);
        System.out.println("video2=>" + video2);
    }
}

​ 这里我们发现,我们确实克隆了一个对象,而且这个对象和它的原型一模一样。可是我们打算修改它的值却无法修改,因为它相当于使用了原来的引用,所有的值都会跟着原来的video1的变化而变化,这个没有意义。

优化

​ 我们可以重写clone()方法,以前是等于直接克隆了这个对象,我们这次把其中的参数值也克隆下来,这样就不会跟着原来对象变化了。

public class Video implements Cloneable{
    private String name;
    private Date createTime;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Object obj = super.clone();
        Video video = (Video) obj;
        //将这个对象的属性也进行克隆
        video.createTime = (Date) this.createTime.clone();
        return obj;
    }

    public Video() {
    }

    public Video(String name, Date createTime) {
        this.name = name;
        this.createTime = createTime;
    }

    public String getName() {
        return name;
    }

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

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    @Override
    public String toString() {
        return "Video{" +
                "name='" + name + '\'' +
                ", createTime=" + createTime +
                '}';
    }
}

​ 测试。

public class BiliBili {
    public static void main(String[] args) throws CloneNotSupportedException {
        //原型对象
        Date date = new Date();
        Video video1 = new Video("设计模式",date);
        Video video2 = (Video) video1.clone();
        System.out.println("video1=>" + video1);
        System.out.println("video2=>" + video2);
        System.out.println("=====================");

        date.setTime(22222);
        System.out.println("video1=>" + video1);
        System.out.println("video2=>" + video2);
    }
}

​ 发现这次不再变化了。

​ 当然我们也可以采用序列化的方式把这个克隆的对象序列化。然后要使用的时候再反序列化。这个方式也是可以的。

四、适配器模式

​ 比如日常生活中,电脑要想上网,需要连接网线,而有的电脑不支持直接连接电脑,那就需要中间转化的适配器。如下图。

image-20210513150010582

​ 那么就模拟一下。

​ 先创建一个网线。请求上网。

public class Adaptee {
    public void request(){
        System.out.println("连接网线上网");
    }
}

​ 再创建电脑。

public class Computer {
    //电脑需要连接转接器
    public void net(Net2Usb adapter){
        //想上网插不上网线,找一个转接头
        adapter.handlerRequest();
    }
}

​ 发现电脑和网线不能直接相连。那么就要创建适配器。先建一个适配器接口。

public interface Net2Usb {
    //处理请求,网线插入usb
    public void handlerRequest();
}

​ 创建适配器。

public class Adapter extends Adaptee implements Net2Usb{
    @Override
    public void handlerRequest() {
        super.request();//可以上网了~
    }
}

​ 测试。

public class Computer {
    //电脑需要连接转接器
    public void net(Net2Usb adapter){
        //想上网插不上网线,找一个转接头
        adapter.handlerRequest();
    }

    public static void main(String[] args) {
        Computer computer = new Computer();
        Adaptee adaptee = new Adaptee();
        Adapter adapter = new Adapter();
        computer.net(adapter);
    }
}

​ 输出。连接网线上网。

优化

​ 上面这根网线被适配器继承了,相当于直接嵌入到这里面去了,我们希望使用任意的适配器。

​ 所以我们不如直接把这个网线引入,而不采用继承的方式。(当然看了狂神的这里觉得继承也没什么区别)。

public class Adapter2 implements Net2Usb{
    private Adaptee adaptee;

    public Adapter2(Adaptee adaptee) {
        this.adaptee = adaptee;
    }

    @Override
    public void handlerRequest() {
        adaptee.request();//可以上网了~
    }
}
public class Computer {
    //电脑需要连接转接器
    public void net(Net2Usb adapter){
        //想上网插不上网线,找一个转接头
        adapter.handlerRequest();
    }

    public static void main(String[] args) {
        Computer computer = new Computer();
        Adaptee adaptee = new Adaptee();
        Adapter2 adapter = new Adapter2(adaptee);
        computer.net(adapter);
    }
}

适配器模式的作用

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

  • 目标接口: 客户所期待的接口, 目标可以是具体的或抽象的类, 可以是接口
  • 需要适配的类: 需要适配的类或适配者类
  • 适配器: 通过包装一个需要适配的对象, 把原接口转换成目标对象

五、桥接模式

​ 比如一个场景有两个维度,比如品牌和类型。苹果手机、苹果电脑,华为手机、华为电脑等等。

image-20210221170558686

​ 如果要新增内容的话,比如新增一种电脑,或者新增一个品牌的话,会非常麻烦,现在的桥接模式有点类似多继承的模式。

​ 代码。品牌接口。

public interface Brand {
    void info();
}

​ 品牌实现。

public class Lenovo implements Brand{
    @Override
    public void info() {
        System.out.println("联想");
    }
}

public class Apple implements Brand{
    @Override
    public void info() {
        System.out.println("苹果");
    }
}

​ 创建电脑。

public abstract class Computer {

    //组合起来
    protected Brand brand;

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

    public void info(){
        //自带品牌
        brand.info();
    }
}

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

    @Override
    public void info() {
        super.info();
        System.out.println("台式机");
    }
}

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

    @Override
    public void info() {
        super.info();
        System.out.println("笔记本");
    }
}

​ 这样把品牌作为这个电脑的一个成员。就可以很容易的解决问题了。

image-20210513154937551

优缺点

​ 好处分析:
​ 1、桥接模式偶尔类似于多继承方案,但是多继承方案违背了类的单一职责原则,复用性比较差,类的个数也非常多,桥接模式是比多继承方案更好的解决方法。极大的减少了子类的个数,从而降低管理和维护的成本。
​ 2、桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统。符合开闭原则,就像一座桥,可以把两个变化的维度连接起来!
​ 劣势分析:
​ 1、桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。
​ 2、桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性。

最佳实践

​ 1、如果一个系统需要在构建的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。抽象化角色和实现化角色可以以继承的方式独立扩展而互不影响,在程序运行时可以动态将一个抽象化子类的对象和一个实现化子类的对象进行组合,即系统需要对抽象化角色和实现化角色进行动态耦合。
​ 2、一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。
​ 3、虽然在系统中使用继承是没有问题的,但是由于抽象化角色和具体化角色需要独立变化,设计要求需要独立管理这两者。对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。

标签:void,System,public,Override,println,狂神,设计模式,大话,out
来源: https://blog.csdn.net/m0_49698209/article/details/116753627

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有