资讯专栏INFORMATION COLUMN

通熟易懂的设计模式(一)

luxixing / 1197人阅读

摘要:维基百科抽象工厂的例子构建模式当构建一个复杂对象时,就可以使用建造者模式。在中,类中的方法就是适配器模式的例子,把一个数组转换为一个集合。这种设计模式的好处是方便添加一种车巴士,只需要继承类。

写在前面

评判一个程序员是否优秀,就是 show me the code。优秀的代码可读性强,高内聚低耦合,可扩展。想要写优秀的代码,做个优秀的程序员,就需要多看看大牛写的开源框架,吸取其中的精华,多学学设计模式,除此之外,没有任何其他捷径。

设计模式主要分为创建型模式结构型模式行为型模式三种类型。

工厂方法(Factory method pattern)

定义一个创建对象的接口,让实现这个接口的类来决定实例化哪个类。工厂方法让类的实例化推迟到子类中进行,它属于创建型模式。

工厂对象通常包含一个或多个方法,用来创建这个工厂所能创建的各种类型的对象。这些方法可能接收参数,用来指定对象创建的方式,最后返回创建的对象。

工厂通常是一个用来创建其他对象的对象。工厂是构造方法的抽象,用来实现不同的分配方案。

维基百科工厂方法的例子

// 定义了 Button 如何创建
public interface Button{}
// 实现了 WinButton 
public class WinButton implements Button{}
// 实现了 MacButton 
public class MacButton implements Button{}

// 创建 Button 的工厂类
public interface ButtonFactory {
    Button createButton();
}
// 真正创建 WinButton 的实现类,实现了 ButtonFactory
public class WinButtonFactory implements ButtonFactory {
    @Override
    public static Button createButton(){
        return new WinButton();
    }
}
// 真正创建 MacButton的实现类,实现了 ButtonFactory
public class MacButtonFactory implements ButtonFactory {
    @Override
    public static Button createButton(){
        return new MacButton();
    }
}
抽象工厂模式(Abstract factory pattern)

将一组具有同一主题的多带带的工厂封装起来。在使用中,使用方需要创建抽象工厂的具体实现,然后使用抽象工厂作为接口来创建这一方法的具体对象。它属于创建型模式。

抽象工厂就好像是对工厂方法的一种扩展,有个产品族的概念,也就是一堆产品。上面的工厂方法就一个产品(Button),而下面抽象工厂的例子里有两个产品(Button和 Border)。

抽象工厂的优点:
具体产品从客户代码中被分离出来。
容易改变产品的系列。
将一个系列的产品族统一到一起创建。

抽象工厂的缺点:
在产品族中扩展新的产品是很困难的,它需要修改抽象工厂的接口。

维基百科抽象工厂的例子

public interface Button {}
public interface Border {}

public class WinButton implements Button {}
public class WinBorder implements Border {}

public class MacButton implements Button {}
public class MacBorder implements Border {}

public interface AbstractFactory {
    Button createButton();
    Border createBorder();
}

public class WinFactory {
    @Override
    public static Button createButton() {
    return new WinButton();
    }
    @Override
    public static Border createBorder() {
    return new WinBorder();
    }
}

public class MacFactory {
    @Override
    public static Button createButton() {
    return new MacButton();
    }
    @Override
    public static Border createBorder() {
    return new MacBorder();
    }
}
构建模式(Builder pattern)

当构建一个复杂对象时,就可以使用建造者模式。它实际上就是传入一个参数,然后返回对象本身,方便下一个属性或者参数来构建。可以按需构建对象,可扩展性强。它属于创建型模式。

在 JDK 中,StringBuilder 类的 append() 方法就是一个很好的构建模式例子。

    // java.lang.StringBuilder
    @Override
    public StringBuilder append(CharSequence s) {
        super.append(s);
        return this;
    }
原型模式(Prototype pattern)

它的特点在于通过 复制 一个已经存在的实例来返回新的实例,而不是新建实例。被复制的实例就是我们所称的原型,这个原型是可定制的。它属于创建型模式。

原型模式多用于创建复杂的或者耗时的实例,因为这种情况下,复制一个已经存在的实例使程序运行更高效,它们实际上就是命名不一样的同类数据。

在 JDK 中,Object 类中的 clone() 方法就是典型的原型模式。

    //在具体的实现类里可以直接重写 clone() 方法
    protected native Object clone() throws CloneNotSupportedException;
单例模式(Singleton pattern)

不管在任何时候,获取一个对象时只会返回同一个实例。通常我们在都读取配置文件时,文件里的内容是不变的,因此我们就可以使用单例模式来实现。它属于创建型模式。

单例模式很简单,只需要三步就完成,下面是 JDK 里的 Runtime 类实现的单例模式。

public class Runtime {
    // 1.new 一个私有的静态的 Runtime 实例
    private static Runtime currentRuntime = new Runtime();

    // 2.返回当前应用的 Runtime 实例
    public static Runtime getRuntime() {
        return currentRuntime;
    }

    // 3.私有化构造方法,不允许在其它类构造 Runtime 实例
    private Runtime() {}
}
适配器模式(Adapter pattern)

适配器模式也称作包装(wrapper),它属于结构型模式。

它的作用是把原本两个不兼容的接口通过一个适配器或者包装成兼容的接口,然后它们可以一起工作。例如我们的手机数据线,就好比是一个适配器,一端是 USB接口,一端是 Type-C 或者 Lightning 接口,本来它们是不能一起工作的,但我们用这跟数据线(适配器)就可以让它们协同工作了。

它的优点:
1.可以让任何两个没有关联的接口一起工作。
2.提高了接口的复用。
3.增加了接口的透明度。
4.灵活性好。

它的缺点:
1.过多地使用适配器,会让系统非常零乱,不易整体进行把握。
2.由于 JAVA 只能继承一个类,所以最多只能适配一个适配者类,而且目标类必须是抽象类。

在 JDK 中,Arrays 类中的 asList(T... a) 方法就是适配器模式的例子,把一个数组转换为一个集合。

    public static  List asList(T... a) {
        return new ArrayList<>(a);
    }

在 JDK 中,Collections 工具类的 list() 方法把枚举转集合。

    public static  ArrayList list(Enumeration e) {
        ArrayList l = new ArrayList<>();
        while (e.hasMoreElements())
            l.add(e.nextElement());
        return l;
    }

当然,这上面两个例子是非常简单的,好像看不出来使用了任何的设计模式,跟我们平时使用的转换一模一样。这里只是一个理念的介绍,实际上,在使用中是用一个中间的 Adapter 类来做兼容或者包装的。

桥接模式(Bridge pattern)

将抽象与其实现分离,以便两者可以独立变化。它属于结构型模式。

当一个类经常变化时,面向对象编程的特性变得非常有用,因为可以用最少的关于程序的先验知识来容易地改变程序的代码。当类和它经常变化时,桥模式很有用。类本身可以被认为是抽象,类可以作为实现来做。桥模式也可以被认为是两层抽象。

它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦。

它的优点:
1.抽象和实现分离开。
2.优秀的扩展能力。
3.实现细节对调用方透明。

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

这是个生产不同车辆和需要不同生产流程的例子。首先,定义一个车辆(Vehicle)的抽象接口,里面有个生产车辆的对应流程集合和一个生产的抽象方法。然后是自行车(Bike)和汽车(Car)对抽象接口的实现。然后定义一个生产车辆需要的流程(WorkShop),它有两个实现 ProduceWorkShop 和 TestWorkShop。最后,main 方法的代码就是对这个桥接模式的使用方式。

这种设计模式的好处是方便添加一种车巴士(Bus),只需要继承 Vehicle 类。也非常方便的添加一种生产流程喷漆(PaintWorkShop),只需要继承 WorkShop 类即可,扩展性很高。

//车辆的抽象接口
public abstract class Vehicle {
    //
    protected List workshops = new ArrayList();
    public Vehicle() {
        super();
    }
    public boolean joinWorkshop(WorkShop workshop) {
        return workshops.add(workshop);
    }
    public abstract void manufacture();
}

//自行车的实现
public class Bike extends Vehicle {
    @Override
    public void manufacture() {
        System.out.println("Manufactoring Bike...");
        workshops.stream().forEach(workshop -> workshop.work(this));
    }
}
//汽车的实现
public class Car extends Vehicle {
    @Override
    public void manufacture() {
        System.out.println("Manufactoring Car");
        workshops.stream().forEach(workshop -> workshop.work(this));
    }
}
//生产车的抽象接口
public abstract class WorkShop {
    public abstract void work(Vehicle vehicle);
}
//制造车的实现
public class ProduceWorkShop extends WorkShop {
    public ProduceWorkShop() {
        super();
    }
    @Override
    public void work(Vehicle vehicle) {
        System.out.print("Producing... ");
    }
}
//测试车的实现
public class TestWorkShop extends WorkShop {
    public TestWorkShop() {
        super();
    }
    @Override
    public void work(Vehicle vehicle) {
        System.out.print("Testing... ");
    }
}
//使用
public class Main {
    public static void main(String[] args) {
        //生产一辆自行车
        Vehicle bike = new Bike();
        bike.joinWorkshop(new ProduceWorkShop());
        bike.manufacture();
        //生产一辆汽车
        Vehicle car = new Car();
        car.joinWorkshop(new ProduceWorkShop());
        car.joinWorkshop(new TestWorkShop());
        car.manufacture();
    }
}

最后,单看设计模式例子是非常简单的,但有时候写代码时却用不上这些设计模式,这是一种写代码思维上的转变。也就是在写代码之前,我们需要根据业务场景思考,那种设计模式适合使用,记住使用设计模式的最终目的是代码可读性强,高内聚低耦合,可扩展。这是一种思维上的转变,多思考在动手,熟能生巧。

PS:
清山绿水始于尘,博学多识贵于勤。
微信公众号:「清尘闲聊」。
欢迎一起谈天说地,聊代码。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/74913.html

相关文章

  • 通熟易懂设计模式(二)

    摘要:提供酒店相关的接口返回该时间段有效的酒店提供航班相关的接口返回该时间段有效的航班提供一个旅行对外的接口,一次返回酒店和航班信息调用旅行外观模式享元模式享元模式主要用于减少创建对象的数量,以减少内存占用和提高性能。 组合模式(Composite pattern) 组合模式看起来就像对象组的树形结构,一个对象里面包含一个或一组其他的对象。它是属于结构型模式。例如,一个公司包括很多个部门,每...

    daydream 评论0 收藏0
  • 浅谈NUXT - 基于vue.js服务端渲染框架

    摘要:是一款基于的服务端渲染框架,跟的异曲同工。该配置项用于定义应用客户端和服务端的环境变量。 Vue因其简单易懂的API、高效的数据绑定和灵活的组件系统,受到很多前端开发人员的青睐。国内很多公司都在使用vue进行项目开发,我们正在使用的简书,便是基于Vue来构建的。 我们知道,SPA前端渲染存在两大痛点:(1)SEO。搜索引擎爬虫难以抓取客户端渲染的页面meta信息和其他SEO相关信息,使...

    yearsj 评论0 收藏0
  • 浅谈NUXT - 基于vue.js服务端渲染框架

    摘要:是一款基于的服务端渲染框架,跟的异曲同工。该配置项用于定义应用客户端和服务端的环境变量。 Vue因其简单易懂的API、高效的数据绑定和灵活的组件系统,受到很多前端开发人员的青睐。国内很多公司都在使用vue进行项目开发,我们正在使用的简书,便是基于Vue来构建的。 我们知道,SPA前端渲染存在两大痛点:(1)SEO。搜索引擎爬虫难以抓取客户端渲染的页面meta信息和其他SEO相关信息,使...

    godiscoder 评论0 收藏0
  • Java后端学习,你应该看那些书籍?

    摘要:全书分三大部分共章第章介绍的基础知识安装和基本语法第章介绍的基本编程机器学习基础及中常用的第三方库函数,并介绍数据预处理的基本方法第章分别介绍常用的机器学习分析算法及深度学习等。每章都采用多个经典案例图文并茂地介绍机器学习的原理和实现方法。 最近在学习Java和全栈开发,推荐一些有用的书籍 书架主要针对Java后端和全栈开发用的 书籍介绍 《Spring Boot 2.0企业级应用开发...

    QiuyueZhong 评论0 收藏0

发表评论

0条评论

luxixing

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<