2Y-

导航

设计模式(1)

(只是一种思维方式)

  • 创建型模式:

    • 单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式

  • 结构性模式

    • 适配器模式、桥接模式、装饰模式、组合模式、享元模式、代理模式

  • 行为型模式

    • 模板方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式、访问者模式

OOP七大原则:

  • 开闭原则: 对拓展开放,对修改关闭

  • 里氏替换原则: 继承必须保证超类所拥有的性质在子类中仍然成立

  • 依赖倒置原则: 要面向接口编程,不要面向现实

  • 单一职责原则: 控制类的粒度大小,将对象解耦,提高内聚(一个方法做一件事)

  • 接口隔离原则: 要用各个类建立他们需要的专用接口

  • 迪米特法则: 只与直接朋友交谈,不与“陌生人”通信

  • 合成复用原则: 尽量先使用组合或者内聚等关联关系来实现,其次才考虑使用继承来实现

工厂模式

作用:实现了船舰这和调用者的分离

分类:

  • 简单工厂模式

    • 用来生产同一等级结构中的任意产品(对于新增的产品,需要球盖已有代码)

  • 工厂方法模式

    • 用来生产同意等级结构中的固定产品(支持增加任意产品)

  • 抽象工厂模式

    • 围绕一个超级工厂创建其他工厂,该超级工厂又称为其他工厂的工厂

OOP七大原则

  • 开闭原则: 对拓展开放,对修改关闭

  • 依赖倒置原则: 要面向接口编程,不要面向现实

  • 迪米特法则: 只与直接朋友交谈,不与“陌生人”通信

核心本质

  • 实例化对象不能用new

  • 将选择实现类,创建对象同意管理和控制。从而将调用者跟我们的实现类解耦

 

简单工厂模式:

例子:顾客买车

车有两种,特斯拉和五菱

先创造一个car的接口

package com.yu.Factoyy.simple;

public interface Car {
   void name();
}

同时创造五菱和特斯拉的类

package com.yu.Factoyy.simple;

public class wuling implements Car {
   @Override
   public void name() {
  }
}
package com.yu.Factoyy.simple;

public class Tesila implements Car {
   @Override
   public void name() {
  }
}

当一个顾客去买车(再顾客类下面调出车名字代替),

package com.yu.Factoyy.simple;

public class Consumer {
   public static void main(String[] args) {
       //接口,所有的实现类!
    Car car = new wuling ();
    Car car2= new Tesila ();

    car.name ();
     car2.name ();
    }
}

这是没使用工厂模式的,顾客能直接接触到车的信息如图所示

image-20200914221107971

但是你想啊,消费者怎么能直接创建出来一个车呢,这不符合常理,这样就引出了简单工厂模式,创建一个车工厂,消费者只需要通过告知车工厂我们需要什么车,就可以了。就不用去自己new一些参数了,加入一个特斯拉有几百上千的参数,岂不是消费者要累死。通过一个工厂直接区get,就省事了许多。如图所示:

image-20200914221407432

 

消费者不用再去关心底层车工厂干了什么,代码实现:

package com.yu.Factoyy.simple;

//静态工厂模式,增加一个新产品的话,不修改代码,做不到
//开闭原则
public class CarFactory {
   
   public static  Car getcar(String car){
       if (car.equals ( "五菱" )){
           return new wuling ();
      }else if (car.equals ( "特斯拉" )){
           return  new Tesila ();
      }else{
           return null;
      }
  }
   
}
package com.yu.Factoyy.simple;

public class Consumer {
   public static void main(String[] args) {
       //使用工厂创建
       Car car = CarFactory.getcar ( "五菱" );
       Car car2 = CarFactory.getcar ( "特斯拉" );

       car.name ();
       car2.name ();

  }
}

但是这时候就出现了另一个问题,如果车厂新增了产品,就假如是大众,那我们不海边代码就无法得到“大众”这个新的类了,这并不符合我们的开闭原则。

 

工厂方法模式:

加入在车工厂中横向增加一个大众,我们直接把车工厂也变成一个接口(说白了又加一层),如图所示

image-20200914225126263

解读这张图:直接把CarFactory和Car都变成接口,这样代码可以进行横向扩展,就像图中所示,增加一个摩拜单车,直接加就行了,不用改变源代码,这样满足了开闭原则,但是代码会变得很多。

代码如下所示:

创建CarFactory的接口:

package com.yu.Factoyy.method;

public interface CarFactory {
   Car getCar();
}
package com.yu.Factoyy.method;

public class TesilaFactory implements CarFactory {
   @Override
   public Car getCar() {
       return new Tesila ();
  }
}

所有的车的工厂都来实现CarFactory,当要增加一个Mobai的时候,则可以直接添加一个MobaiFactory类和Mobai类,然后在Consumer中直接调就好。代码如下:

Mobai类:

package com.yu.Factoyy.method;

public class Mobai implements Car {
    public void name(){
        System.out.println ("摩拜单车");
    }
}

 

MobaiFactory类:

package com.yu.Factoyy.method;

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

Consumer类:

package com.yu.Factoyy.method;

public class Consumer {
    public static void main(String[] args) {
        Car car = new wulingFactory ().getCar ();
        Car car1 = new TesilaFactory ().getCar ();

        car.name ();
        car1.name ();

        Car car2 = new MobaiFactory ().getCar ();
        car2.name ();
    }
}

结果不放了,能实现。

 

结构复杂度:低

代码复杂度:低

编程复杂度:低

管理上的复杂度:低

根据设计原则,选择工厂方法模式;根据实际业务,简单工厂模式

 

抽象工厂模式:

定义:抽象工厂模式提供了一个创建一系列相关或者相互依赖对象的接口,无需指定他们的具体类

适用场景:

  • 客户端(应用层)不依赖于产品类实例如何被创建、实现等细节

  • 强调一系列相关的产品对象(属于同意产品族)一起使用创建对象需要大量的重复代码

  • 提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体的实现

优点:

  • 具体产品在应用层的代码隔离,无需关心创建的细节

  • 将一个系列的产品统一到一起创建

缺点:

  • 规定了所有可能被创建的产品集合,产品簇中扩展新的产品困难;

  • 增加了系统的抽象性和理解难度

UMl类图:

image-20200915002625234

用小米和华为举例:

image-20200915005710349

如图所示,同一个品牌的路由器属于同一个产品族,同样类型的产品又属于同样的产品等级结构。

代码实现:

首先定义一个手机的接口:

package com.yu.Factoyy.abstrate;

//手机产品接口
public interface IphoneProduct {

    void start();
    void shutup();
    void callup();
    void sendSMS();

}

还有路由器产品的接口:

package com.yu.Factoyy.abstrate;

//路由器产品接口
public interface IRouterProduct {

    void start();
    void shutup();
    void openWiFI();
    void setting();

}

小米和华为都生产手机和路由器,这时候要分别写响应的类,小米手机和小米路由器,华为手机和华为路由器

华为路由器:

package com.yu.Factoyy.abstrate;

import com.sun.deploy.panel.IProperty;

public class HuaweiRouter implements IRouterProduct {

    @Override
    public void start() {
        System.out.println ("华为路由器开机");
    }

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

    @Override
    public void openWiFI() {
        System.out.println ("华为路由器开wifi");
    }

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

    }
}

华为手机:

package com.yu.Factoyy.abstrate;

//华为手机
public class HuaweiPhone implements IphoneProduct{
    @Override
    public void start() {
        System.out.println ("华为开机");
    }

    @Override
    public void shutup() {
        System.out.println ("华为关机");

    }

    @Override
    public void callup() {
        System.out.println ("华为打电话");

    }

    @Override
    public void sendSMS() {
        System.out.println ("华为发短信");

    }
}

小米路由器:

package com.yu.Factoyy.abstrate;

public class XiaomiRouter implements IRouterProduct {
    @Override
    public void start() {
        System.out.println ("小米路由器开机");
    }

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

    @Override
    public void openWiFI() {
        System.out.println ("小米路由器开wifi");
    }

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

小米手机:

package com.yu.Factoyy.abstrate;

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

    @Override
    public void shutup() {
        System.out.println ("小米关机");

    }

    @Override
    public void callup() {
        System.out.println ("小米打电话");

    }

    @Override
    public void sendSMS() {
        System.out.println ("小米发短信");

    }
}

然后,小米和华为都有自己的工厂来生产这些产品,这时候就需要一个工厂接口以及两个工厂的实现类:

工厂接口:

package com.yu.Factoyy.abstrate;

public interface IProductFactory {

    //生产手机
    IphoneProduct iphoneProduct();

    //生产路由器
    IRouterProduct iRouterProduct();

}

小米工厂:

package com.yu.Factoyy.abstrate;

public class xiaomiFactory implements IProductFactory {
 	@Override
    public IphoneProduct iphoneProduct() {
        return new xiaomiPhone ();
    }

    @Override
    public IRouterProduct iRouterProduct() {
        return new xiaomiRouter ();
    }
}

华为工厂:

package com.yu.Factoyy.abstrate;

public class HuaweiFactory implements IProductFactory {
    @Override
    public IphoneProduct iphoneProduct() {
        return new HuaweiPhone ();
    }

    @Override
    public IRouterProduct iRouterProduct() {
        return new HuaweiRouter ();
    }
}

最后在定义一个客户来查看结果:

package com.yu.Factoyy.abstrate;

public class Client {
    public static void main(String[] args) {
        System.out.println ("=========小米产品=====");
        XiaomiFactory xiaomiFactory = new XiaomiFactory ();

        IphoneProduct iphoneProduct = xiaomiFactory.iphoneProduct ();

        iphoneProduct.callup ();
        iphoneProduct.sendSMS ();
        iphoneProduct.shutup ();
        iphoneProduct.start ();

        IRouterProduct iRouterProduct = xiaomiFactory.iRouterProduct ();

        iRouterProduct.openWiFI ();
        iRouterProduct.setting ();
        iRouterProduct.shutup ();
        iRouterProduct.start ();


        System.out.println ("=========华为产品=====");
        HuaweiFactory huaweiFactory  = new HuaweiFactory ();

        iphoneProduct = huaweiFactory.iphoneProduct ();

        iphoneProduct.callup ();
        iphoneProduct.sendSMS ();
        iphoneProduct.shutup ();
        iphoneProduct.start ();

        iRouterProduct = huaweiFactory.iRouterProduct ();

        iRouterProduct.openWiFI ();
        iRouterProduct.setting ();
        iRouterProduct.shutup ();
        iRouterProduct.start ();




    }
}

结果图:

image-20200915010504835

抽象工厂模式,第一个优点,产品在应用层就进行了就进行了隔离,最后的Client不需要关心他们是怎么创建的,需要了直接去工厂里面拿就好了。第二个优点,同样的小米手机和华为手机我们不用去管,有需要找工厂。

缺点,加入想继续生产新的产品,例如像生产电脑,需要改动大量代码,很不方便。

简单模式和方法模式主要关心的是产品的等级,最后的抽象模式责视更多的关心产品族,相对来说效率更高,因为他不是一个工厂生产一个产品,是生产一个族。

 

◆简单工厂模式(静态工厂模式) ◆虽然某种程度上不符合设计原则,但实际使用最多! ◆工厂方法模式 ◆不修改已有类的前提下,通过增加新的工厂类实现扩展。 ◆抽象工厂模式 ◆不可以增加产品,可以增加产品族!

 

建造者模式

  • 建造者模式也属于创建型模式,它提供了一种创建对象的最佳方式。

  • 定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示

    用户只需要给出指定复杂对象的类型和内容建造者模式负责按顺序创建复杂对象(把内部的建造过程和细节隐藏起来)

  • 主要作用:在用户不知道对象的建造过程和细节的情况下就可以直接创建复杂的对象。

  • 用户只需要给出指定复杂对象的类型和内容,建造者模式负责按顺序创建复杂对象(把内部的建造过程和细节隐藏起来)

  • 例子: 工厂(建造者模式)∶负责制造汽车(组装过>程和细节在工厂内) 汽车购买者(用户)∶你只需要说出你需要的>型号(对象的类型和内容),然后直接购买就可以使 了(不需要知道汽车是怎么组装的(车轮、车门、发动机、方向盘等等))

  • 角色分析:

  • image-20200915152519035

  • 以盖房为例:盖房简化为以下步骤:打地基、搭框架、接水电、刷墙;首先要找一个工程队。工程队(指挥者)指挥工人(建造的人)来盖房子(产品),最后验收。

  •  

代码实现:

首先得有一个抽象的建造的方法

package com.yu.Factoyy.builder;

//这是一个抽象的建造方法
public abstract class Builder {

    //用来代替盖房的四步
    abstract void buildA();
    abstract void buildB();
    abstract void buildC();
    abstract void buildD();
	//完工的到产品
    abstract Product getProduct();
}

最后的产品是个房子,所以应该有一个产品类,具体的房子有了,然后去建立他

package com.yu.Factoyy.builder;

//盖完的房子
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 + '\'' +
                '}';
    }
}

得有领导去指挥工人,同时也需要工人来完成房子

工人类如下:

package com.yu.Factoyy.builder;

//实际的建造者
public class Worker extends Builder {

    private  Product product;

    //为何事new一个Product:这里的产品是工人来创建的,而不是传给工人的,所以用new
    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;
    }
}

直到这里,房子并没有真正的被搭建出来,因为没人指挥

领导类如下:

package com.yu.Factoyy.builder;

//指挥,核心,负责指挥一个工程,如何创造,由他决定
public class Director {

    //指挥工人按照顺序盖房子
    public Product build(Builder builder){
        builder.buildA ();
        builder.buildB ();
        builder.buildC ();
        builder.buildD ();

        return builder.getProduct ();
    }

}

最后写一个顾客看房子的类(测试类)

package com.yu.Factoyy.builder;

public class test {
    public static void main(String[] args) {
        //领导
        Director director = new Director ();
        //领导指挥具体工人完成房子
        Product build = director.build ( new Worker () );
        System.out.println ( build.toString ());
    }
}

对于“使得同样的构建过程可以创建不同的表示“的理解:看上想的ABCD四个Bulider,这仅仅代表着四个不同的方法,就很好理解,着盖房子的步骤能盖平方,也能盖瓦房。

 

以上的例子事常规用法,领导类在建造者模式种有很重要的作用,用于指挥具体构建这如何构建产品,控制先后此u,像调用者返回完整的产品类,但是有些情况下需要简化系统,把领导类和抽象建造者进行结合。

举个简单的例子,去点餐,服务员(具体的建造者)可以随意搭配几样吃的(零件)来组成一款套餐(产品),出售给客户,比盖房子少了一个指挥者,这种方式直接把指挥者交给客户来操作,更见简单灵活。

通过静态内部类方式实现零件无需装配构造,这种方式更加领过,更符合定义。内部又复杂对象的默认实现,使用时可以根据用户需求自由定义更改内容,不需改变具体的构造方式,就可以身长出不同的产品。

结合之后的代码如下:

首先跟盖房子相同,得有一个建造者的抽象类

package com.yu.Factoyy.builder.demo02;

//建造者
public abstract class Builder {

    //msg是为了用户方便传参
    abstract Builder builderA(String msg);
    abstract Builder builderB(String msg);
    abstract Builder builderC(String msg);
    abstract Builder builderD(String msg);

    abstract Product getProduct();
}

String msg是为了后面用户方便传参数

 

有一个产品类

package com.yu.Factoyy.builder.demo02;

//产品,套餐A
public class Product {
    private String BuildA="套餐A";
    private String BuildB="套餐B";
    private String BuildC="套餐C";
    private String BuildD="套餐D";

    public String getBuildA() {
        return BuildA;
    }

    public void setBuildA(String buildA) {
        BuildA = buildA;
    }

    public String getBuildB() {
        return BuildB;
    }

    public void setBuildB(String buildB) {
        BuildB = buildB;
    }

    public String getBuildC() {
        return BuildC;
    }

    public void setBuildC(String buildC) {
        BuildC = buildC;
    }

    public String getBuild() {
        return BuildD;
    }

    public void setBuild(String build) {
        BuildD = build;
    }

    @Override
    public String toString() {
        return "Product{" +
                "BuildA='" + BuildA + '\'' +
                ", BuildB='" + BuildB + '\'' +
                ", BuildC='" + BuildC + '\'' +
                ", Build='" + BuildD + '\'' +
                '}';
    }
}

然后需要具体的服务员去完成套餐:

package com.yu.Factoyy.builder.demo02;

import com.yu.Factoyy.abstrate.IProductFactory;

//具体的建造者
public class Worker extends Builder{


    private Product product;

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

    @Override
    Builder builderA(String msg) {
        product.setBuildA ( msg );
        return this;
    }

    @Override
    Builder builderB(String msg) {
        product.setBuildB ( msg );
        return this;
    }

    @Override
    Builder builderC(String msg) {
        product.setBuildC ( msg );
        return this;
    }

    @Override
    Builder builderD(String msg) {
        product.setBuild ( msg );
        return this;

    }

    @Override
    Product getProduct() {
        return product;
    }

}

测试类(点单的人),在这个模式中,将上一个模式中的领导和客户相结合,即是客户又是领导。

package com.yu.Factoyy.builder.demo02;

public class Test {
    public static void main(String[] args) {
        //服务员
        Worker worker = new Worker ();
        //链式编程
        Product product = worker.builderA ("雪碧").builderB ("芬达").getProduct ();

        System.out.println (product.toString ());
    }
}

固定直接选择套餐的话,就不需要加上.builderA,因为你观察workr的方法,他不止能点产品还能去去builderA,直接通过Worker用户可以自行选择,除了套餐之外,它可以出现一些自定义的操作。

测试结果:

image-20200915215115241

建造者模式的优缺点:

优点

  1.**客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。**

  2.每一个具体建造者都独立,因此可以方便地替换具体建造者或增加新的具体建造者, 用户使用不同的具体建造者即可得到不同的产品对象

  3.可以更加精细地控制产品的创建过程 。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。

  4.增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合“开闭”。

缺点

  1.当建造者过多时,会产生很多类,难以维护。

  2.建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,若产品之间的差异性很大,则不适合使用该模式,因此其使用范围受到一定限制。

  3.若产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。

 

  • 应用场景:

    • 需要生成的产品对象有复杂的内部结构,这些产品对象具备共性;

    • 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。

    • 适合于一个具有较多的零件(属性)的产品(对象)的创建过程。

  • 建造者模式和工厂模式的对比:

    • 与抽象工厂模式相比,建造者模式返回一个组装好的完整产品,而抽象工厂模式返回一系列相关的产品,这些产品位于不同的产品等级结构,构成了一个产品族。

    • 在抽象工厂模式中,客户端实例化工厂类,然后调用工厂方法获取所需产品对象,而在建造者模式中,客户端可以不直接调用建造者的相关方法,而是通过指挥者类来指导如何生成对象,包括对象的组装过程和建造步骤,它侧重于一步步构造一个复杂对象,返回一个完整的对象。

    • 如果将抽象工厂模式看成汽车配件生产工厂,生产一个产品族的产品,那么建造者模式就是一个汽车组装工厂,通过对部件的组装可以返回一辆完整的汽车!

 

 

posted on 2020-09-25 12:07  2Y-  阅读(110)  评论(0编辑  收藏  举报