大话设计模式(Java代码)

第一章 简单工厂模式

​ 代码无错便是优?非也,想要达到高质量代码的水平,不止要满足当前的需求,程序不容易扩展,

不容易维护,不容易复用,也谈不上高质量。

​ 为什么四大发明里是活字印刷而不是印刷术,因为活字印刷是思想上的胜利,可维护、可复用、可修改、可扩展。

​ 面向对象的思维,使用封装继承多态把程序的耦合性降低,使用设计模式使得程序更加的灵活。

三种类型的设计模式:

序号 模式 & 描述 包括
1 创建型模式 这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵00活。 工厂模式(Factory Pattern)抽象工厂模式(Abstract Factory Pattern)单例模式(Singleton Pattern)建造者模式(Builder Pattern)原型模式(Prototype Pattern)
2 结构型模式 这些设计模式关注类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式。 适配器模式(Adapter Pattern)桥接模式(Bridge Pattern)过滤器模式(Filter、Criteria Pattern)组合模式(Composite Pattern)装饰器模式(Decorator Pattern)外观模式(Facade Pattern)享元模式(Flyweight Pattern)代理模式(Proxy Pattern)
3 行为型模式 这些设计模式特别关注对象之间的通信。 责任链模式(Chain of Responsibility Pattern)命令模式(Command Pattern)解释器模式(Interpreter Pattern)迭代器模式(Iterator Pattern)中介者模式(Mediator Pattern)备忘录模式(Memento Pattern)观察者模式(Observer Pattern)状态模式(State Pattern)空对象模式(Null Object Pattern)策略模式(Strategy Pattern)模板模式(Template Pattern)访问者模式(Visitor Pattern)
4 J2EE 模式 这些设计模式特别关注表示层。这些模式是由 Sun Java Center 鉴定的。 MVC 模式(MVC Pattern)业务代表模式(Business Delegate Pattern)组合实体模式(Composite Entity Pattern)数据访问对象模式(Data Access Object Pattern)前端控制器模式(Front Controller Pattern)拦截过滤器模式(Intercepting Filter Pattern)服务定位器模式(Service Locator Pattern)传输对象模式(Transfer Object Pattern)

UML类图:

图中每个部分的含义:

  • “动物”矩形框:代表一个类,类图分三层,第一层是类的名称,如果是抽象类用斜体显示,第二层是类的字段和属性,第三层是类的方法和行为,注意符号: +代表public - 代表private # 代表protected
  • 左下角的飞翔是接口,接口顶端有《interface》显示,第二行是接口方法,接口还有棒棒糖表示法,如图中右下角唐老鸭实现了讲人话接口

类与类,类与接口之间的关系怎么描述:

  1. 继承关系:空心三角形+实线 如鸟继承了动物
  2. 实现接口:空心三角形+虚线 如大雁实现飞翔接口
  3. 关联关系:实线箭头 当一个类知道另一个类时,就是关联关系,如企鹅知道气候
  4. 聚合关系:空心菱形+实线箭头 聚合表示一种弱的“拥有”关系,A包含B,B不是A的一部分,如大雁和雁群,每只大雁都属于一个雁群。
  5. 合成关系:实心菱形+实线箭头 两端的数字为基数,表示这一端的类可以有几个实例,合成时一种强拥有的关系,如鸟拥有翅膀
  6. 依赖关系:虚线箭头表示 如动物需要氧气和水才能新陈代谢

简单工厂

工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

介绍

**意图:**定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。

**主要解决:**主要解决接口选择的问题。

**何时使用:**我们明确地计划不同条件下创建不同实例时。

**如何解决:**让其子类实现工厂接口,返回的也是一个抽象的产品。

**关键代码:**创建过程在其子类执行。

应用实例: 1、您需要一辆汽车,可以直接从工厂里面提货,而不用去管这辆汽车是怎么做出来的,以及这个汽车里面的具体实现。 2、Hibernate 换数据库只需换方言和驱动就可以。

优点: 1、一个调用者想创建一个对象,只要知道其名称就可以了。 2、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。 3、屏蔽产品的具体实现,调用者只关心产品的接口。

**缺点:**每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。

使用场景: 1、日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。 2、数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。 3、设计一个连接服务器的框架,需要三个协议,“POP3”、“IMAP”、“HTTP”,可以把这三个作为产品类,共同实现一个接口。

**注意事项:**作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。

实现

我们将创建一个 Shape 接口和实现 Shape 接口的实体类。下一步是定义工厂类 ShapeFactory

FactoryPatternDemo 类使用 ShapeFactory 来获取 Shape 对象。它将向 ShapeFactory 传递信息(CIRCLE / RECTANGLE / SQUARE),以便获取它所需对象的类型。

步骤:

1.创建一个接口

package cn.itcast.factory;

public interface Shape {
    void draw();
}

2.创建接口的各个实现类

package cn.itcast.factory;

public class Circle implements Shape{
    @Override
    public void draw(){
        System.out.println("this is a circle,we draw it");
    }
}

package cn.itcast.factory;

public class Square implements Shape {
    @Override
    public void draw(){
        System.out.println("this is a square,we draw it");
    }

}

package cn.itcast.factory;

public class Rectangle implements Shape{
    @Override
    public void draw(){
        System.out.println("this is a rectangle,we draw it");
    }
}

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

package cn.itcast.factory;

public class ShapeFactory {
    //使用getShape方法获取形状类型的对象
    public Shape getShape(String shapeType){
        if(shapeType== null){
            return null;
        }
        if(shapeType.equalsIgnoreCase("CIRCLE")){
            return new Circle();
        } else if(shapeType.equalsIgnoreCase("RECTANGLE")){
            return new Rectangle();
    } else if (shapeType.equalsIgnoreCase("SQUARE")){
        return new Square();
        }
        return null;
}
}

4.使用该工厂,通过传递类型信息来获取实体类的对象

package cn.itcast.factory;

public class FactoryPatternDemo {
    public static void main(String[] args) {
        ShapeFactory sf = new ShapeFactory();
        Shape shape1= sf.getShape("CIRCLE");
        shape1.draw();
        Shape shape2= sf.getShape("RECTANGLE");
        shape2.draw();
        Shape shape3= sf.getShape("SQUARE");
        shape3.draw();
    }
}

5.执行程序,输出结果

第二章 商场促销—策略模式

在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。策略模式封装了变化。

在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。

**意图:**定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。

**主要解决:**在有多种算法相似的情况下,使用 if…else 所带来的复杂和难以维护。

**何时使用:**一个系统有许多许多类,而区分它们的只是他们直接的行为。

**如何解决:**将这些算法封装成一个一个的类,任意地替换。

**关键代码:**实现同一个接口。

应用实例: 1、诸葛亮的锦囊妙计,每一个锦囊就是一个策略。 2、旅行的出游方式,选择骑自行车、坐汽车,每一种旅行方式都是一个策略。 3、JAVA AWT 中的 LayoutManager。

优点: 1、算法可以自由切换。 2、避免使用多重条件判断。 3、扩展性良好。

缺点: 1、策略类会增多。 2、所有策略类都需要对外暴露。

使用场景: 1、如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。(比如商场促销,用打折或返利,其实都是一些算法,重要的是这些算法随时都可能相互替换的) 2、一个系统需要动态地在几种算法中选择一种。 3、如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。

**注意事项:**如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题。

实现:

我们将创建一个定义活动的 Strategy 接口和实现了 Strategy 接口的实体策略类。Context 是一个使用了某种策略的类。StrategyPatternDemo,我们的演示类使用 Context 和策略对象来演示 Context 在它所配置或使用的策略改变时的行为变化。

步骤1:创建一个接口。

package Strategy;

public interface Strategy {
    public int doOperation(int num1,int num2);
}

步骤2:创建实现接口的实现类

package Strategy;

public class OperationAdd implements Strategy {
    
    @Override
    public int doOperation(int num1, int num2) {
        return num1+num2;
    }
}


package Strategy;

public class OperationSubtract implements Strategy {
    @Override
    public int doOperation(int num1, int num2) {
        return num1-num2;
    }
}


package Strategy;

public class OperationMultiply  implements Strategy{
    @Override
    public int doOperation(int num1, int num2) {
        return num1*num2;
    }
}

步骤3:创建 Context

package Strategy;

public class Context {
    private Strategy strategy;
    
    public Context(Strategy strategy){
        this.strategy = strategy;
    }
    
    public int executeStrategy(int num1,int num2){
        return strategy.doOperation(num1,num2);
    }
}

步骤4:使用 Context 来查看当它改变策略 Strategy 时的行为变化。

package Strategy;

public class StrategyPatternDemo {
    public static void main(String[] args) {
        Context context = new Context(new OperationAdd());
        System.out.println("10 + 5 = " + context.executeStrategy(10, 5));

        context = new Context(new OperationSubtract());
        System.out.println("10 - 5 = " + context.executeStrategy(10, 5));

        context = new Context(new OperationMultiply());
        System.out.println("10 * 5 = " + context.executeStrategy(10, 5));
    }
}

步骤5:执行程序,输出结果:

10 + 5 = 15
10 - 5 = 5
10 * 5 = 50

第三章 单一职责原则

菜鸟教程里设计模式六大原则:

1、开闭原则(Open Close Principle)

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

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

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

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

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

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

这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。它还有另外一个意思是:降低类之间的耦合度。由此可见,其实设计模式就是从大型软件架构出发、便于升级和维护的软件设计思想,它强调降低依赖,降低耦合。

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

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

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

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

单一职责原则:就一个类而言,应该仅有一个引起它变化的原因。

如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。这种耦合会导致脆弱的设计,当发生变化时,设计会遭受意想不到的破坏。

软件设计真正要做的许多内容,就是发现职责并把这些职责相互分离。如果你能够想到多于一个类的动机去改变一个类,那么这个类就具有多于一个的职责。就应该考虑类的职责分离。

第四章 开放封闭原则

开放-封闭原则:是说软件实体(类、函数、模块等等),应该可以扩展,但是不可修改。

有两个特征:一是对扩展是开放的,二是对于更改是封闭的。也就是说,你设计的时候,时刻要考虑,尽量让这个类足够好,写好了就不要再去修改了,如果新需求来,我们增加一些类就完事儿了,原来的代码能不动则不动。

在我们最初编写代码时,假设变化不会发生,当变化发生时,我们就创建抽象来隔离以后发生的同类变化。

开放封闭原则时面向对象设计的核心所在,遵循这个原则可以带来面向对象技术所声称的巨大好处,也就是可维护、可扩展、可复用、灵活性好。开发人员应该仅对程序设计中呈现出频繁变化的那部分做抽象。

第五章 依赖倒转原则

依赖倒转原则:抽象不该依赖细节,细节应该依赖抽象。针对接口编程,不要针对实现编程。

就是谁也不依靠谁,除了约定的接口,大家都可以灵活自如。

A: 高层模块不应该依赖低层模块,两个都应该依赖抽象

B:抽象不该依赖细节,细节应该依赖抽象

就像CPU、内存条一样,不应该依赖具体的主板,内存坏了,也不该影响其他部件如果高层和低层都依赖抽象,就是接口,只要接口是稳定的,那么任何一个更改都不用担心其他收到影响。

PC机里各个零件易拔插:就是高内聚,低耦合。收音机的耦合性强就造成了维修困难。

里氏代换原则

子类必须可以替代他们的父类型,在软件里面,把父类都替换为他的子类,程序的行为没有变化。

只有当子类可以替换掉父类,软件单位的功能不受影响时,父类才能真正被复用,而子类也能够在父类的基础上增加新的行为。

正是由于这个原则,子类的可替换性才使得父类类型的模块在无需修改的情况下可以扩展。才使得开放-封闭原则成为了可能。

第六章-装饰模式

装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。

这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。

我们通过下面的实例来演示装饰器模式的用法。其中,我们将把一个形状装饰上不同的颜色,同时又不改变形状类。

介绍

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

**主要解决:**一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。

**何时使用:**在不想增加很多子类的情况下扩展类。

**如何解决:**将具体功能职责划分,同时继承装饰者模式。

关键代码: 1、Component 类充当抽象角色,不应该具体实现。 2、修饰类引用和继承 Component 类,具体扩展类重写父类方法。

应用实例: 1、孙悟空有 72 变,当他变成"庙宇"后,他的根本还是一只猴子,但是他又有了庙宇的功能。 2、不论一幅画有没有画框都可以挂在墙上,但是通常都是有画框的,并且实际上是画框被挂在墙上。在挂在墙上之前,画可以被蒙上玻璃,装到框子里;这时画、玻璃和画框形成了一个物体。

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

**缺点:**多层装饰比较复杂。

使用场景: 1、扩展一个类的功能。 2、动态增加功能,动态撤销。

**注意事项:**可代替继承。

实现:

我们将创建一个 Shape 接口和实现了 Shape 接口的实体类。然后我们创建一个实现了 Shape 接口的抽象装饰类 ShapeDecorator,并把 Shape 对象作为它的实例变量。

RedShapeDecorator 是实现了 ShapeDecorator 的实体类。

DecoratorPatternDemo 类使用 RedShapeDecorator 来装饰 Shape 对象。

步骤1:

创建一个接口

package Decorator;

public interface Shape {
     void draw();
}

步骤2:

创建实现接口的实体类。

package Decorator;

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

package Decorator;

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

步骤3:

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

package Decorator;

public abstract class ShapeDecorator implements Shape {
    protected Shape decoratedShape;
    public ShapeDecorator(Shape decoratedShape){
        this.decoratedShape= decoratedShape;
    }

    public void draw(){    //这个draw方法还是没有覆盖重写,要靠其子类来实现装饰
        decoratedShape.draw();
    }
}

步骤4:

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

package Decorator;

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");
    }
}

步骤5:

使用 RedShapeDecorator 来装饰 Shape 对象

package Decorator;

import org.w3c.dom.ls.LSOutput;

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();    // 使用了修饰子类覆盖重写的draw方法

        System.out.println("\nRectangle of the border");
        redRectangle.draw();
    }

}


结果:
Circle with normal border
Shape: Circle

Circle of red border
Shape: Circle
Border color:Red

Rectangle of the border
Shape: Rectangle
Border color:Red

总结:装饰模式是为已有功能动态的添加更多功能的一种方式。为了避免一些不常用的修饰功能增加到原来的类,从而增加主类的复杂度,把每个要修饰的功能放到单独的类中,并让这个类包装它所需要的对象。优点就是把类的核心职责和装饰功能区分开了。

第七章 代理模式

在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。

在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。

介绍:

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

**主要解决:**在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。

**何时使用:**想在访问一个类时做一些控制。

**如何解决:**增加中间层。

**关键代码:**实现与被代理类组合。

应用实例: 1、Windows 里面的快捷方式。 2、猪八戒去找高翠兰结果是孙悟空变的,可以这样理解:把高翠兰的外貌抽象出来,高翠兰本人和孙悟空都实现了这个接口,猪八戒访问高翠兰的时候看不出来这个是孙悟空,所以说孙悟空是高翠兰代理类。 3、买火车票不一定在火车站买,也可以去代售点。 4、一张支票或银行存单是账户中资金的代理。支票在市场交易中用来代替现金,并提供对签发人账号上资金的控制。 5、spring aop。

优点: 1、职责清晰。 2、高扩展性。 3、智能化。

缺点: 1、由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。 2、实现代理模式需要额外的工作,有些代理模式的实现非常复杂。

**使用场景:**按职责来划分,通常有以下使用场景: 1、远程代理。 2、虚拟代理。 3、Copy-on-Write 代理。 4、保护(Protect or Access)代理。 5、Cache代理。 6、防火墙(Firewall)代理。 7、同步化(Synchronization)代理。 8、智能引用(Smart Reference)代理。

注意事项: 1、和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。 2、和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制。

实现:

我们将创建一个 Image 接口和实现了 Image 接口的实体类。ProxyImage 是一个代理类,减少 RealImage 对象加载的内存占用。

ProxyPatternDemo 类使用 ProxyImage 来获取要加载的 Image 对象,并按照需求进行显示。

步骤1:创建一个接口

package Proxy;

public interface Image {
     void display();
}

步骤2:创建实现接口的实体类

package Proxy;
// 真正的图片类
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);
    }
}

package Proxy;
// 图片类的代理类
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();
    }
}

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

package Proxy;

public class ProxyPatternDemo {

    public static void main(String[] args) {
        Image image = new ProxyImage("test_10mb.jpg");

        // 图像将从磁盘加载
        image.display();
        System.out.println("");
        // 图像不需要从磁盘加载
        image.display();
    }
}

//结果
Loading test_10mb.jpg
Displaying test_10mb.jpg

Displaying test_10mb.jpg

代理模式其实就是在访问对象时引入一定的间接性,因为这种间接性,可以附加多种用途。

远程代理:为一个对象在不同的地址空间提供局部代表,这样可以隐藏一个对象存在于不同空间的事实。

虚拟代理:是根据需要创建开销很大的对象,通过它来存放实例化需要很长时间的真实对象。比如浏览器打开网页时的图片,使用虚拟代理真实的图片。

安全代理:用来控制真实对象访问时的权限。

智能代理:调用真实的对象时,处理另外一些事。

第八章 工厂方法模式(抽象工厂)

抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。

介绍:

**意图:**提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

**主要解决:**主要解决接口选择的问题。

**何时使用:**系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。

**如何解决:**在一个产品族里面,定义多个产品。

**关键代码:**在一个工厂里聚合多个同类产品。

**应用实例:**工作了,为了参加一些聚会,肯定有两套或多套衣服吧,比如说有商务装(成套,一系列具体产品)、时尚装(成套,一系列具体产品),甚至对于一个家庭来说,可能有商务女装、商务男装、时尚女装、时尚男装,这些也都是成套的,即一系列具体产品。假设一种情况(现实中是不存在的,要不然,没法进入共产主义了,但有利于说明抽象工厂模式),在您的家中,某一个衣柜(具体工厂)只能存放某一种这样的衣服(成套,一系列具体产品),每次拿这种成套的衣服时也自然要从这个衣柜中取出了。用 OOP 的思想去理解,所有的衣柜(具体工厂)都是衣柜类的(抽象工厂)某一个,而每一件成套的衣服又包括具体的上衣(某一具体产品),裤子(某一具体产品),这些具体的上衣其实也都是上衣(抽象产品),具体的裤子也都是裤子(另一个抽象产品)。

**优点:**当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。

**缺点:**产品族扩展非常困难,要增加一个系列的某一产品,既要在抽象的 Creator 里加代码,又要在具体的里面加代码。

使用场景: 1、QQ 换皮肤,一整套一起换。 2、生成不同操作系统的程序。

**注意事项:**产品族难扩展,产品等级易扩展。

实现:

我们将创建 ShapeColor 接口和实现这些接口的实体类。下一步是创建抽象工厂类 AbstractFactory。接着定义工厂类 ShapeFactoryColorFactory,这两个工厂类都是扩展了 AbstractFactory。然后创建一个工厂创造器/生成器类 FactoryProducer

AbstractFactoryPatternDemo 类使用 FactoryProducer 来获取 AbstractFactory 对象。它将向 AbstractFactory 传递形状信息 ShapeCIRCLE / RECTANGLE / SQUARE),以便获取它所需对象的类型。同时它还向 AbstractFactory 传递颜色信息 ColorRED / GREEN / BLUE),以便获取它所需对象的类型。

步骤1:为形状定义一个接口

package factory;

public interface Shape {
    void draw();
}

步骤2:定义接口的实现类

package factory;

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


package factory;

public class Square implements Shape{
    @Override
    public void draw() {
        System.out.println("Inside Square::draw() method.");
    }
}

package factory;

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

步骤3:为颜色创建一个接口

package factory;

public interface Color {
    void fill();
}

步骤4:创建颜色接口的实现类

package factory;

public class Blue implements Color{
    @Override
    public void fill() {
        System.out.println("Inside Blue::fill() method.");
    }
}

package factory;

public class Red implements Color{
    @Override
    public void fill() {
        System.out.println("Inside Red::fill() method.");
    }
}

package factory;

public class Green implements Color{
    @Override
    public void fill() {
        System.out.println("Inside Blue::fill() method.");
    }
}

步骤5:为 Color 和 Shape 对象创建抽象类来获取工厂。

package factory;

public abstract class AbstractFactory {
    public abstract Color getColor(String color);
    public abstract Shape getShape(String shape);
}

步骤6:创建扩展了 AbstractFactory 的工厂类,基于给定的信息生成实体类的对象。

package factory;

public class ShapeFactory extends AbstractFactory {
    @Override
    public Color getColor(String color) {
        return null;
    }

    @Override
    public Shape getShape(String shapeType) {
        if (shapeType == null){
            return null;
        }
        if(shapeType.equalsIgnoreCase("CIRCLE")){
            return new Circle();
        } else if(shapeType.equalsIgnoreCase("RECTANGE")){
            return new Rectangle();
        } else if(shapeType.equalsIgnoreCase("SQUARE")){
            return new Square();
        }
        return null;
    }
}

package factory;

public class ColorFactory extends AbstractFactory {

    @Override
    public Shape getShape(String shapeType){
        return null;
    }

    @Override
    public Color getColor(String color) {
        if(color == null){
            return null;
        }
        if(color.equalsIgnoreCase("RED")){
            return new Red();
        } else if(color.equalsIgnoreCase("GREEN")){
            return new Green();
        } else if(color.equalsIgnoreCase("BLUE")){
            return new Blue();
        }
        return null;
    }
}

步骤7:创建一个工厂创造器/生成器类,通过传递形状或颜色信息来获取工厂。

package factory;

public class FactoryProducer {
   public static AbstractFactory getFactory(String choice){
       if(choice.equalsIgnoreCase("SHAPE")){
           return new ShapeFactory();
       } else if(choice.equalsIgnoreCase("COLOR")){
           return new ColorFactory();
       }
       return null;
   }
}

步骤8:使用 FactoryProducer 来获取 AbstractFactory,通过传递类型信息来获取实体类的对象。

package factory;

public class AbstractFactoryPatternDemo {
    public static void main(String[] args) {

        //获取形状工厂
        AbstractFactory shapeFactory = FactoryProducer.getFactory("SHAPE");

        //获取形状为 Circle 的对象
        Shape shape1 = shapeFactory.getShape("CIRCLE");

        //调用 Circle 的 draw 方法
        shape1.draw();

        //获取形状为 Rectangle 的对象
        Shape shape2 = shapeFactory.getShape("RECTANGLE");

        //调用 Rectangle 的 draw 方法
        shape2.draw();

        //获取形状为 Square 的对象
        Shape shape3 = shapeFactory.getShape("SQUARE");

        //调用 Square 的 draw 方法
        shape3.draw();

        //获取颜色工厂
        AbstractFactory colorFactory = FactoryProducer.getFactory("COLOR");

        //获取颜色为 Red 的对象
        Color color1 = colorFactory.getColor("RED");

        //调用 Red 的 fill 方法
        color1.fill();

        //获取颜色为 Green 的对象
        Color color2 = colorFactory.getColor("Green");

        //调用 Green 的 fill 方法
        color2.fill();

        //获取颜色为 Blue 的对象
        Color color3 = colorFactory.getColor("BLUE");

        //调用 Blue 的 fill 方法
        color3.fill();
    }
}

// 结果:
Inside Circle::draw() method.
Inside Rectangle::draw() method.
Inside Square::draw() method.
Inside Red::fill() method.
Inside Green::fill() method.
Inside Blue::fill() method.

因为简单工厂违背了开放封闭原则,需要在工厂类里直接修改,使用抽象工厂可以解决。(依赖倒转原则)

工厂方法模式是简单工厂的进一步抽象和推广,由于使用了多态性,工厂方法模式保持了简单工厂模式的优点,并且克服了他的缺点,但缺点是由于每加一个产品,就需要加一个产品工厂的类,增加了额外的开发量。

第九章 原型模式

等着学完Java拷贝再说

第十一章 迪米特法则

迪米特法则(LOD):如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用,如果其中的一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。

迪米特法则其根本思想,是强调了类之间的松耦合。我们在程序设计时,类之间的耦合越弱,越有利于复用,一个处在弱耦合的类被修改,不会对有关系的类造成波及。

你可能感兴趣的:(计算机基础,java,设计模式,开发语言)