JAVA设计模式简介及八种常见设计模式详解

一、什么是设计模式

一、什么是设计模式                                                                                                                                       

  设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是它能被广泛应用的原因。简单说:

  模式:在某些场景下,针对某类问题的某种通用的解决方案。

  场景:项目所在的环境

  问题:约束条件,项目目标等

  解决方案:通用、可复用的设计,解决约束达到目标。

二、设计模式的六大原则

  因为设计模式就是基于这些原则的实现,所以很有必要了解这些原则,下面主要对面向对象编程的几个原则进行简单介绍。

  1、开闭原则(Open Close Principle)

  开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。

  2、里氏代换原则(Liskov Substitution Principle)

  里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。

  3、依赖倒转原则(Dependence Inversion Principle)

  这个是开闭原则的基础,具体内容:真对接口编程,依赖于抽象而不依赖于具体。

  4、接口隔离原则(Interface Segregation Principle)

  这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。

  5、迪米特法则(最少知道原则)(Demeter Principle)

  为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。

  6、合成复用原则(Composite Reuse Principle)

  原则是尽量使用合成/聚合的方式,而不是使用继承。

三、设计模式的三个分类                                                                                                                                

  创建型模式:对象实例化的模式,创建型模式用于解耦对象的实例化过程。

  结构型模式:把类或对象结合在一起形成一个更大的结构。

  行为型模式:类和对象如何交互,及划分责任和算法。

  如下图所示:

 

四、各分类中模式的关键点      

  创建型模式                                                                                                            

  单例模式:某个类只能有一个实例,提供一个全局的访问点。

  简单工厂:一个工厂类根据传入的参量决定创建出那一种产品类的实例。

  工厂方法:定义一个创建对象的接口,让子类决定实例化那个类。

  抽象工厂:创建相关或依赖对象的家族,而无需明确指定具体类。

  建造者模式:封装一个复杂对象的构建过程,并可以按步骤构造。

  原型模式:通过复制现有的实例来创建新的实例。

  结构性模式

  适配器模式:将一个类的方法接口转换成客户希望的另外一个接口。

  组合模式:将对象组合成树形结构以表示“”部分-整体“”的层次结构。

  装饰模式:动态的给对象添加新的功能。

  代理模式:为其他对象提供一个代理以便控制这个对象的访问。

  亨元(蝇量)模式:通过共享技术来有效的支持大量细粒度的对象。

  外观模式:对外提供一个统一的方法,来访问子系统中的一群接口。

  桥接模式:将抽象部分和它的实现部分分离,使它们都可以独立的变化。

   行为型模式

  模板模式:定义一个算法结构,而将一些步骤延迟到子类实现。

  解释器模式:给定一个语言,定义它的文法的一种表示,并定义一个解释器。

  策略模式:定义一系列算法,把他们封装起来,并且使它们可以相互替换。

  状态模式:允许一个对象在其对象内部状态改变时改变它的行为。

  观察者模式:对象间的一对多的依赖关系。

  备忘录模式:在不破坏封装的前提下,保持对象的内部状态。

  中介者模式:用一个中介对象来封装一系列的对象交互。

  命令模式:将命令请求封装为一个对象,使得可以用不同的请求来进行参数化。

  访问者模式:在不改变数据结构的前提下,增加作用于一组对象元素的新功能。

  责任链模式:将请求的发送者和接收者解耦,使的多个对象都有处理这个请求的机会。

  迭代器模式:一种遍历访问聚合对象中各个元素的方法,不暴露该对象的内部结构。

五、八种常见设计模式详解 

  1、单例模式

  所谓的单例设计指的是一个类只允许产生一个实例化对象。 
  最好理解的一种设计模式,分为懒汉式饿汉式

  1.1、饿汉式

  ——构造方法私有化,外部无法产生新的实例化对象,只能通过static方法取得实例化对象

class Singleton {
    /**
     * 在类的内部可以访问私有结构,所以可以在类的内部产生实例化对象
     */
    private static Singleton instance = new Singleton();
    /**
     * private 声明构造
     */
    private Singleton() {

    }
    /**
     * 返回对象实例
     */
    public static Singleton getInstance() {
        return instance;
    }

    public void print() {
        System.out.println("Hello Singleton...");
    }
}

   1.2、懒汉式

  ——当第一次去使用Singleton对象的时候才会为其产生实例化对象的操作

class Singleton {

    /**
     * 声明变量
     */
    private static volatile Singleton singleton = null;

    /**
     * 私有构造方法
     */
    private Singleton() {

    }

    /**
     * 提供对外方法
     * @return 
     */
    public static Singleton getInstance() {
        // 还未实例化
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
    public void print() {
        System.out.println("Hello World");
    }
}

  当多个线程并发执行 getInstance 方法时,懒汉式会存在线程安全问题,所以用到了 synchronized 来实现线程的同步,当一个线程获得锁的时候其他线程就只能在外等待其执行完毕。而饿汉式则不存在线程安全的问题。

  2、工厂设计模式

  工厂模式分为工厂方法模式和抽象工厂模式。

  工厂方法模式

工厂方法模式: 
1. 工厂方法模式分为三种:普通工厂模式,就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建。 
2. 多个工厂方法模式,是对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象。 
3. 静态工厂方法模式,将上面的多个工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可。

  2.1、普通工厂模式

  建立一个工厂类,对实现了同一接口的一些类进行实例的创建。

interface Sender {
    void Send();
}

class MailSender implements Sender {

    @Override
    public void Send() {
        System.out.println("This is mail sender...");
    }
}

class SmsSender implements Sender {

    @Override
    public void Send() {
        System.out.println("This is sms sender...");
    }
}

public class FactoryPattern {
    public static void main(String[] args) {
        Sender sender = produce("mail");
        sender.Send();
    }
    public static Sender produce(String str) {
        if ("mail".equals(str)) {
            return new MailSender();
        } else if ("sms".equals(str)) {
            return new SmsSender();
        } else {
            System.out.println("输入错误...");
            return null;
        }
    }
}

   2.2、 多个工厂方法模式

  该模式是对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象。

interface Sender {
    void Send();
}

class MailSender implements Sender {

    @Override
    public void Send() {
        System.out.println("This is mail sender...");
    }
}

class SmsSender implements Sender {

    @Override
    public void Send() {
        System.out.println("This is sms sender...");
    }
}

class SendFactory {
    public Sender produceMail() {
        return new MailSender();
    }

    public Sender produceSms() {
        return new SmsSender();
    }
}

public class FactoryPattern {
    public static void main(String[] args) {
        SendFactory factory = new SendFactory();
        Sender sender = factory.produceMail();
        sender.Send();
    }
}

   2.3、 静态工厂方法模式

  将上面的多个工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可。

interface Sender {
    void Send();
}

class MailSender implements Sender {

    @Override
    public void Send() {
        System.out.println("This is mail sender...");
    }
}

class SmsSender implements Sender {

    @Override
    public void Send() {
        System.out.println("This is sms sender...");
    }
}

class SendFactory {
    public static Sender produceMail() {
        return new MailSender();
    }

    public static Sender produceSms() {
        return new SmsSender();
    }
}

public class FactoryPattern {
    public static void main(String[] args) {
        Sender sender = SendFactory.produceMail();
        sender.Send();
    }
}

   2.4、抽象工厂模式

  工厂方法模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要扩展程序,必须对工厂类进行修改,这违背了闭包原则,所以,从设计角度考虑,有一定的问题,如何解决? 
  那么这就用到了抽象工厂模式,创建多个工厂类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。

interface Provider {
    Sender produce();
}

interface Sender {
    void Send();
}

class MailSender implements Sender {

    public void Send() {
        System.out.println("This is mail sender...");
    }
}

class SmsSender implements Sender {

    public void Send() {
        System.out.println("This is sms sender...");
    }
}

class SendMailFactory implements Provider {

    public Sender produce() {
        return new MailSender();
    }
}

class SendSmsFactory implements Provider {

    public Sender produce() {
        return new SmsSender();
    }
}


public class FactoryPattern {
    public static void main(String[] args) {
        Provider provider = new SendMailFactory();
        Sender sender = provider.produce();
        sender.Send();
    }
}

   3、 建造者模式

  工厂类模式提供的是创建单个类的模式,而建造者模式则是将各种产品集中起来管理,用来创建复合对象,所谓复合对象就是指某个类具有不同的属性。

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

/**
 * @Author: LiuWang
 * @Created: 2018/8/6 17:47
 */

abstract class Builder {
    /**
     * 第一步:装CPU
     */
   public abstract void buildCPU();

    /**
     * 第二步:装主板
     */
    public abstract void buildMainBoard();

    /**
     * 第三步:装硬盘
     */
    public abstract void buildHD();

    /**
     * 获得组装好的电脑
     * @return
     */
    public abstract Computer getComputer();
}

/**
 * 装机人员装机
 */
class Director {
    public void Construct(Builder builder) {
        builder.buildCPU();
        builder.buildMainBoard();
        builder.buildHD();
    }
}

/**
 * 具体的装机人员
 */
class ConcreteBuilder extends  Builder {

    Computer computer = new Computer();

    @Override
    public void buildCPU() {
        computer.Add("装CPU");
    }

    @Override
    public void buildMainBoard() {
        computer.Add("装主板");
    }

    @Override
    public void buildHD() {
        computer.Add("装硬盘");
    }

    @Override
    public Computer getComputer() {
        return computer;
    }
}

class Computer {

    /**
     * 电脑组件集合
     */
    private List<String> parts = new ArrayList<String>();

    public void Add(String part) {
        parts.add(part);
    }

    public void print() {
        for (int i = 0; i < parts.size(); i++) {
            System.out.println("组件:" + parts.get(i) + "装好了...");
        }
        System.out.println("电脑组装完毕...");
    }
}

public class BuilderPattern {

    public static void main(String[] args) {
        Director director = new Director();
        Builder builder = new ConcreteBuilder();
        director.Construct(builder);
        Computer computer = builder.getComputer();
        computer.print();
    }
}

   4、 适配器设计模式

  适配器模式是将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的的类的兼容性问题。

  主要分三类:类的适配器模式、对象的适配器模式、接口的适配器模式。

  4.1、 类的适配器模式:

class Source {
    public void method1() {
        System.out.println("This is original method...");
    }
}

interface Targetable {

    /**
     * 与原类中的方法相同
     */
    public void method1();

    /**
     * 新类的方法
     */
    public void method2();
}

class Adapter extends Source implements Targetable {

    @Override
    public void method2() {
        System.out.println("This is the targetable method...");
    }
}

public class AdapterPattern {
    public static void main(String[] args) {
        Targetable targetable = new Adapter();
        targetable.method1();
        targetable.method2();
    }
}

   4.2、 对象的适配器模式

  基本思路和类的适配器模式相同,只是将Adapter 类作修改,这次不继承Source 类,而是持有Source 类的实例,以达到解决兼容性的问题。

class Source {
    public void method1() {
        System.out.println("This is original method...");
    }
}

interface Targetable {

    /**
     * 与原类中的方法相同
     */
    public void method1();

    /**
     * 新类的方法
     */
    public void method2();
}

class Wrapper implements Targetable {

    private Source source;

    public Wrapper(Source source) {
        super();
        this.source = source;
    }

    @Override
    public void method1() {
        source.method1();
    }

    @Override
    public void method2() {
        System.out.println("This is the targetable method...");
    }
}

public class AdapterPattern {
    public static void main(String[] args) {
        Source source = new Source();
        Targetable targetable = new Wrapper(source);
        targetable.method1();
        targetable.method2();
    }
}

   4.3、 接口的适配器模式

  接口的适配器是这样的:有时我们写的一个接口中有多个抽象方法,当我们写该接口的实现类时,必须实现该接口的所有方法,这明显有时比较浪费,因为并不是所有的方法都是我们需要的,有时只需要某一些,此处为了解决这个问题,我们引入了接口的适配器模式,借助于一个抽象类,该抽象类实现了该接口,实现了所有的方法,而我们不和原始的接口打交道,只和该抽象类取得联系,所以我们写一个类,继承该抽象类,重写我们需要的方法就行。

/**
 * 定义端口接口,提供通信服务
 */
interface Port {
    /**
     * 远程SSH端口为22
     */
    void SSH();

    /**
     * 网络端口为80
     */
    void NET();

    /**
     * Tomcat容器端口为8080
     */
    void Tomcat();

    /**
     * MySQL数据库端口为3306
     */
    void MySQL();
}

/**
 * 定义抽象类实现端口接口,但是什么事情都不做
 */
abstract class Wrapper implements Port {
    @Override
    public void SSH() {

    }

    @Override
    public void NET() {

    }

    @Override
    public void Tomcat() {

    }

    @Override
    public void MySQL() {

    }
}

/**
 * 提供聊天服务
 * 需要网络功能
 */
class Chat extends Wrapper {
    @Override
    public void NET() {
        System.out.println("Hello World...");
    }
}

/**
 * 网站服务器
 * 需要Tomcat容器,Mysql数据库,网络服务,远程服务
 */
class Server extends Wrapper {
    @Override
    public void SSH() {
        System.out.println("Connect success...");
    }

    @Override
    public void NET() {
        System.out.println("WWW...");
    }

    @Override
    public void Tomcat() {
        System.out.println("Tomcat is running...");
    }

    @Override
    public void MySQL() {
        System.out.println("MySQL is running...");
    }
}

public class AdapterPattern {

    private static Port chatPort = new Chat();
    private static Port serverPort = new Server();

    public static void main(String[] args) {
        // 聊天服务
        chatPort.NET();

        // 服务器
        serverPort.SSH();
        serverPort.NET();
        serverPort.Tomcat();
        serverPort.MySQL();
    }
} 
  5、 装饰模式

  顾名思义,装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例。

interface Shape {
    void draw();
}

/**
 * 实现接口的实体类
 */
class Rectangle implements Shape {

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

class Circle implements Shape {

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

/**
 * 创建实现了 Shape 接口的抽象装饰类。
 */
abstract class ShapeDecorator implements Shape {
    protected Shape decoratedShape;

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

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

/**
 *  创建扩展自 ShapeDecorator 类的实体装饰类。
 */
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 DecoratorPattern {
    public static void main(String[] args) {
        Shape circle = new Circle();
        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();
    }
}

   6、 策略模式

  策略模式定义了一系列算法,并将每个算法封装起来,使他们可以相互替换,且算法的变化不会影响到使用算法的客户。需要设计一个接口,为一系列实现类提供统一的方法,多个实现类实现该接口,设计一个抽象类(可有可无,属于辅助类),提供辅助函数。策略模式的决定权在用户,系统本身提供不同算法的实现,新增或者删除算法,对各种算法做封装。因此,策略模式多用在算法决策系统中,外部用户只需要决定用哪个算法即可。

/**
 * 抽象算法的策略类,定义所有支持的算法的公共接口
 */
abstract class Strategy {
    /**
     * 算法方法
     */
    public abstract void AlgorithmInterface();
}

/**
 * 具体算法A
 */
class ConcreteStrategyA extends Strategy {
    //算法A实现方法
    @Override
    public void AlgorithmInterface() {
        System.out.println("算法A的实现");
    }
}

/**
 * 具体算法B
 */
class ConcreteStrategyB extends Strategy {
    /**
     * 算法B实现方法
     */
    @Override
    public void AlgorithmInterface() {
        System.out.println("算法B的实现");
    }
}

/**
 * 具体算法C
 */
class ConcreteStrategyC extends Strategy {
    @Override
    public void AlgorithmInterface() {
        System.out.println("算法C的实现");
    }
}

/**
 * 上下文,维护一个对策略类对象的引用
 */
class Context {
    Strategy strategy;

    public Context(Strategy strategy) {
        this.strategy = strategy;
    }

    public void contextInterface(){
        strategy.AlgorithmInterface();
    }
}

/**
 * 客户端代码:实现不同的策略
 */
public class StrategyPattern {
    public static void main(String[] args) {

        Context context;

        context = new Context(new ConcreteStrategyA());
        context.contextInterface();

        context = new Context(new ConcreteStrategyB());
        context.contextInterface();

        context = new Context(new ConcreteStrategyC());
        context.contextInterface();
    }
}

  7、 代理模式

  代理模式指给一个对象提供一个代理对象,并由代理对象控制对原对象的引用。代理可以分为静态代理和动态代理。通过代理模式,可以利用代理对象为被代理对象添加额外的功能,以此来拓展被代理对象的功能。可以用于计算某个方法执行时间,在某个方法执行前后记录日志等操作。

  7.1、 静态代理

  静态代理需要我们写出代理类和被代理类,而且一个代理类和一个被代理类一一对应。代理类和被代理类需要实现同一个接口,通过聚合使得代理对象中有被代理对象的引用,以此实现代理对象控制被代理对象的目的。

/**
 * 代理类和被代理类共同实现的接口
 */
interface IService {

    void service();
}


/**
 * 被代理类
 */
class Service implements IService{

    @Override
    public void service() {
        System.out.println("被代理对象执行相关操作");
    }
}

/**
 * 代理类
 */
class ProxyService implements IService{
    /**
     * 持有被代理对象的引用
     */
    private IService service;

    /**
     * 默认代理Service类
     */
    public ProxyService() {
        this.service = new Service();
    }

    /**
     * 也可以代理实现相同接口的其他类
     * @param service
     */
    public ProxyService(IService service) {
        this.service = service;
    }

    @Override
    public void service() {
        System.out.println("开始执行service()方法");
        service.service();
        System.out.println("service()方法执行完毕");
    }
}


//测试类
public class ProxyPattern {

    public static void main(String[] args) {
        IService service = new Service();
        //传入被代理类的对象
        ProxyService proxyService = new ProxyService(service);
        proxyService.service();
    }
}

   7.2、 动态代理

  JDK 1.3 之后,Java通过java.lang.reflect包中的三个类Proxy、InvocationHandler、Method来支持动态代理。动态代理常用于有若干个被代理的对象,且为每个被代理对象添加的功能是相同的(例如在每个方法运行前后记录日志)。动态代理的代理类不需要我们编写,由Java自动产生代理类源代码并进行编译最后生成代理对象。 

  创建动态代理对象的步骤: 
  1)指明一系列的接口来创建一个代理对象 
  2)创建一个调用处理器(InvocationHandler)对象 
  3)将这个代理指定为某个其他对象的代理对象 
  4) 在调用处理器的invoke()方法中采取代理,一方面将调用传递给真实对象,另一方面执行各种需要的操作

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

/**
 * 代理类和被代理类共同实现的接口
 */
interface IService {
    void service();
}

class Service implements IService{

    @Override
    public void service() {
        System.out.println("被代理对象执行相关操作");
    }
}

class ServiceInvocationHandler implements InvocationHandler {

    /**
     * 被代理的对象
     */
    private Object srcObject;

    public ServiceInvocationHandler(Object srcObject) {
        this.srcObject = srcObject;
    }

    @Override
    public Object invoke(Object proxyObj, Method method, Object[] args) throws Throwable {
        System.out.println("开始执行"+method.getName()+"方法");
        //执行原对象的相关操作,容易忘记
        Object returnObj = method.invoke(srcObject,args);
        System.out.println(method.getName()+"方法执行完毕");
        return returnObj;
    }
}

public class ProxyPattern {
    public static void main(String[] args) {
        IService service = new Service();
        Class<? extends IService> clazz = service.getClass();

        IService proxyService = (IService) Proxy.newProxyInstance(clazz.getClassLoader(),
                                        clazz.getInterfaces(), new ServiceInvocationHandler(service));
        proxyService.service();
    }
}

   8、模板方法模式

定义:定义了一个算法的骨架,而将一些步骤延迟到子类中,模版方法使得子类可以在不改变算法结构的情况下,重新定义算法的步骤。

   需求:简单描述一下:本公司有程序猿、测试、HR、项目经理等人,下面使用模版方法模式,记录下所有人员的上班情况 

  模板方法模式中的三类角色: 

    1、具体方法(Concrete Method) 

    2、抽象方法(Abstract Method) 

    3、钩子方法(Hook Method) 

 // 具体方法
 public final void workOneDay() {
     Log.e("workOneDay", "-----------------work start----------------");
     enterCompany();
     work();
     exitCompany();
     Log.e("workOneDay", "-----------------work end----------------");
 }
 // 工作  抽象方法
 public abstract void work();
 // 钩子方法
 public boolean isNeedPrintDate() {
     return false;
 }
 private void exitCompany() {
     if (isNeedPrintDate()) {
         Log.e("exitCompany", "---" + new Date().toLocaleString() + "--->");
     }
     Log.e("exitCompany", name + "---离开公司");
 }

 

 /**
  * 重写父类的此方法,使可以查看离开公司时间
  */
 @Override
 public boolean isNeedPrintDate() {
     return true;
 }

 最后测试: 

  查看所有人员的工作情况:

QAWorker qaWorker = new QAWorker("测试人员");
 qaWorker();
 HRWorker hrWorker = new HRWorker("莉莉姐");
 hrWorker.workOneDay();
...
 

   查看程序猿离开公司的时间:

ITWorker itWorker = new ITWorker("jingbin");
itWorker.workOneDay();

  

参考:

  https://www.cnblogs.com/pony1223/p/7608955.html

   https://blog.csdn.net/gfuugff/article/details/86641373?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-4.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-4.channel_param

  https://www.cnblogs.com/Diyo/p/11415588.html

  https://blog.csdn.net/wmq880204/article/details/75106848