大话设计模式学习笔记(15)——抽象工厂模式

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/q1052196521/article/details/79689620

源码git地址 https://github.com/dlovetco/designMode
前面我们学过简单工厂,工厂方法两种“工厂”有关的设计模式。今天让我们来看看这个抽象工厂是怎么回事。

问题提出

小明和小红是夫妻,他们决定在装修的时候提出两种室内设计。因为谁也不能说服谁,所以这两种设计都要保留,不到最后一刻都说不准采用哪一种方法。此外需要小红和小明都给出一个装修模型。要求用代码实现上述场景,并且做到修改最少的代码就可以切换设计方法。

(设装修房子分为装修卧室,装修厨房两部分)

抽象工厂

package abstractfactory;

public class AbstractFactory {

    public static void main(String[] args) {
        //先使用小明的方法
        Kitchen kitchen = new MingDecorate().decorateKitchen();
        Bedroom bedroom = new MingDecorate().decorateBedroom();
        System.out.println(kitchen);
        System.out.println(bedroom);

        //再使用小红的方法
        kitchen = new HongDecorate().decorateKitchen();
        bedroom = new HongDecorate().decorateBedroom();
        System.out.println(kitchen);
        System.out.println(bedroom);
    }
}

/**
 * 装饰方法
 */
interface DecorateFactory {

    Bedroom decorateBedroom();

    Kitchen decorateKitchen();
}

/**
 * 未被装修的厨房
 */
interface Kitchen {

}

class MingKitchen implements Kitchen {
    @Override
    public String toString() {
        return "小明装修出来的厨房";
    }
}

class HongKitchen implements Kitchen {
    @Override
    public String toString() {
        return "小红装修出来的厨房";
    }
}

interface Bedroom {

}

class MingBedroom implements Bedroom {
    @Override
    public String toString() {
        return "小明装修出来的卧室";
    }
}

class HongBedroom implements Bedroom {
    @Override
    public String toString() {
        return "小红装修出来的卧室";
    }
}


class MingDecorate implements DecorateFactory {

    @Override
    public Bedroom decorateBedroom() {
        return new MingBedroom();
    }

    @Override
    public Kitchen decorateKitchen() {
        return new MingKitchen();
    }
}

class HongDecorate implements DecorateFactory {

    @Override
    public Bedroom decorateBedroom() {
        return new HongBedroom();
    }

    @Override
    public Kitchen decorateKitchen() {
        return new HongKitchen();
    }
}

这其实就是抽象工厂模式了。我们可以看到切换两种装修模式的代价 仅仅是修改工厂的实例类型(即MingDecorate还是HongDecorate)。这是依赖倒转好处的体现。

我们再简单分析一下上述代码。
1. 首先看题目,我们发现虽然有两种不同的装修方式,但都需要装修卧室和厨房。所以我们可以把这两种方法抽象出来作为一个接口(DecorateFactroy),这样的话通过依赖倒转,我们切换模式只需要修改接口的实例。
2. 有了装修方法,我们就可以通过装修方法来产生每种方法自定义的实例。这里比如:MingKitchen,HongKitchen。于是我们根据依赖倒转,也可以抽象出一个接口Kitchen。Washroom跟Kichen,此处不再赘述。

plantuml:

@startuml
interface DecorateFactory{
{abstract}Bedroom decorateBedroom()
{abstract}Kitchen decorateKitchen()
}
DecorateFactory <|.. MingDecorate
class MingDecorate{
Bedroom decorateBedroom()
Kitchen decorateKitchen()
}
DecorateFactory <|.. HongDecorate
class HongDecorate{
Bedroom decorateBedroom()
Kitchen decorateKitchen()
}
Kitchen <.. DecorateFactory
interface Kitchen{
}
Kitchen <|.. MingKitchen
class MingKitchen{
}
Kitchen <|.. HongKitchen
class HongKitchen{
}
Bedroom <.. DecorateFactory
interface Bedroom{
}
Bedroom <|.. MingBedroom
class MingBedroom{
}
Bedroom <|.. HongBedroom
class HongBedroom{
}
@enduml

这里写图片描述

利用简单工厂来优化抽象工厂

我们看抽象工厂其实是很像工厂方法的。区别在于工厂方法用于制造不同的对象。而抽象工厂用于制造同一个对象,而在制造过程中有不同的做法(即多种不同的实现方法)。

抽象工厂的缺点

  1. 与客户端的耦合过重,客户端中有许多具体的类。
  2. 虽然说已经在切换方法上我们所要付出的代价足够小,但是我们还是需要在实例化工厂的时候修改。如果在多个地方实例化了工厂对象,则我们还是要修改多个地方。
    下面用简单工厂来优化
package abstractfactory.simplefactory;

public class SimpleFactory {

    public static void main(String[] args) {
        DecorateFactory decorateFactory = new DecorateFactory();
        Kitchen kitchen = decorateFactory.createKitchen();
        Bedroom bedroom = decorateFactory.createBedroom();
        System.out.println(kitchen);
        System.out.println(bedroom);
    }
}

/**
 * 未被装修的厨房
 */
interface Kitchen {

}

class MingKitchen implements Kitchen {
    @Override
    public String toString() {
        return "小明装修出来的厨房";
    }
}

class HongKitchen implements Kitchen {
    @Override
    public String toString() {
        return "小红装修出来的厨房";
    }
}

interface Bedroom {

}

class MingBedroom implements Bedroom {
    @Override
    public String toString() {
        return "小明装修出来的卧室";
    }
}

class HongBedroom implements Bedroom {
    @Override
    public String toString() {
        return "小红装修出来的卧室";
    }
}

class DecorateFactory {
    private static String type = "小明";

    Kitchen createKitchen() {
        switch (type) {
            case "小明":
                return new MingKitchen();
            case "小红":
                return new HongKitchen();
            default:
                return null;
        }
    }

    Bedroom createBedroom() {
        switch (type) {
            case "小明":
                return new MingBedroom();
            case "小红":
                return new HongBedroom();
            default:
                return null;
        }
    }
}

这样的话如果要修改只需要,在DecorateFactory中修改String字段为“小红”。付出的代价比原版抽象工厂要小,且与客户端的耦合性大大减少。因为实例的switch判断移交给了简单工厂。

使用反射再次优化

反射是什么,在这里就不详细解释了。在java中,反射可以通过类名来实例化特定的类。所以我们可以通过传入类名参数,来抛弃掉switch。
这里写图片描述
我的目录结构如上图所示。

package abstractfactory.reflect;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Properties;

public class Reflect {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IOException {
        DecorateFactory decorateFactory = new DecorateFactory();
        Kitchen kitchen = decorateFactory.createKitchen();
        Bedroom bedroom = decorateFactory.createBedroom();
        System.out.println(kitchen);
        System.out.println(bedroom);
    }
}

/**
 * 未被装修的厨房
 */
interface Kitchen {

}

class MingKitchen implements Kitchen {
    @Override
    public String toString() {
        return "小明装修出来的厨房";
    }
}

class HongKitchen implements Kitchen {
    @Override
    public String toString() {
        return "小红装修出来的厨房";
    }
}

interface Bedroom {

}

class MingBedroom implements Bedroom {
    @Override
    public String toString() {
        return "小明装修出来的卧室";
    }
}

class HongBedroom implements Bedroom {
    @Override
    public String toString() {
        return "小红装修出来的卧室";
    }
}

class DecorateFactory {
    private static String type = "小明";
    private Properties properties = new Properties();

    Kitchen createKitchen() throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException {
        properties.load(new InputStreamReader(new FileInputStream("./src/abstractfactory/reflect/test.properties"), "utf-8"));
        String className = properties.getProperty("kitchenMean");
        return (Kitchen) Class.forName("abstractfactory.reflect." + className).newInstance();
    }

    Bedroom createBedroom() throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException {
        properties.load(new InputStreamReader(new FileInputStream("./src/abstractfactory/reflect/test.properties"), "utf-8"));
        String className = properties.getProperty("bedRoomMean");
        return (Bedroom) Class.forName("abstractfactory.reflect." + className).newInstance();
    }
}

test.properties

# 卧室装修方法
bedRoomMean=HongBedroom
# 厨房装修方法
kitchenMean = HongKitchen

上述代码使用反射读取properties里面的参数。这样在切换的时候只要修改properties里面的参数就可以了。代码也不需要重新编译

猜你喜欢

转载自blog.csdn.net/q1052196521/article/details/79689620