设计模式(2)——创建型——工厂相关:简单工厂(Simple factory),工厂方法(Factory method),抽象工厂(Abstract factory)

概要

  • 这里试图描述23个设计模式中的两个工厂(Factory)相关的设计模式:工厂方法(Factorymethod),抽象工厂(Abstract factory)。
  • 注意点:
  1. 这两个都属于创建型设计模式。
  2. 由于这两个设计模式都是建立在简单工厂(Simple factory)模式之上。我们先介绍简单工厂模式,值得注意的是简单工厂模式并不包含在23个设计模式之中。
  3. 文章介绍顺序:简单工厂,工厂方法,抽象工厂。

简单工厂

  • 定义:由一个工厂对象决定创建出哪一种产品类的实例。
  • 类型:创建型,但不属于GoF23种设计模式。
  • 适用场景:工厂类负责创建的对象比较少;客户端(应用层)只知道工厂类的传参,对于如何创建对象(逻辑)不关心
  • 优点:只需要传入正确参数,就可以获取我们所需的对象,而无须知道其具体细节。
  • 缺点:工厂类的职责相对过重,增加新的产品需要修改工厂类的判断逻辑,违背开闭原则。

预设场景:书店现在有三类书籍:IT,Art,Literature。每种书都能被厂家生产。这三种书(三个类)有公共的抽象方法produce。我们现在想要得到这三个类。并且调用它们的公共方法。

首先声明一个抽象类,有一个抽象方法,让这三个类都继承这个类。同时声明一个工厂类,负责接收外部的传参,并根据外部的参数,返回不同的类。于是可以得到如下UML图:
简单工厂模式例UML图

通过图我们很容易地看懂几个类之间的关系,下面是这些类的代码:

/*公用的抽象类*/
public abstract class Book {
    public abstract void produce();
}
/*********************三种书类**********************/

//IT类
public class ITBook extends Book {                   
    @Override
    public void produce() {
        System.out.println("Produce a IT book");
    }
}
//Art类
public class ArtBook extends Book {
    @Override
    public void produce() {
        System.out.println("Produce a Art book");
    }
}
//Literature类
public class LiteratureBook extends Book {
    @Override
    public void produce() {
        System.out.println("Produce a Literature book");
    }
}
/*******************重点:工厂类,负责以上三个类的对象生产****************/
public class BookFactory {
	// 通过外部已知类来生产对象实例
    public static Book produceBook(Class c){
        Book book = null;
        try {
            book = (Book) Class.forName(c.getName()).newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return book;
    }
	//通过传入字符串来判断生产哪种对象
    public static Book produceBook(String bookType){

        if( "IT".equalsIgnoreCase(bookType) )
            return new ITBook();
        else if( "Art".equalsIgnoreCase(bookType) )
            return new ArtBook();
        else if( "Literature".equalsIgnoreCase(bookType) )
            return new LiteratureBook();

        return null;
    }
}
/*******************测试类************************/
public class Test {
    public static void main(String[] args) {
        //通过字符串判断对象类型
        Book book = BookFactory.produceBook("IT");
        book.produce();
        
		//通过Class类来判断类型
        Book book1 = BookFactory.produceBook( ArtBook.class );
        book1.produce();
        
		//推荐设置factory类内函数为static类型,这样不必像下面那样“生产”前必须要先实例化一个工厂类。
        BookFactory factory = new BookFactory();
        Book book2 = BookFactory.produceBook( LiteratureBook.class );
        book2.produce();

    }
}

这里值得注意的是我们让Factory类内部的对外函数为static,这样方便外部直接生成相关类的实例,而不需要先为Factory类实例化。

工厂方法

定义:定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类;工厂方法让类的实例化推迟到子类中进行。
类型:创建型。
适用场景:
	1. 创建对象需要大量重复的代码
	2. 客户端(应用层)不依赖产品类实例如何被创建,实现等细节。
	3. 一个类通过其子类来指定创建哪个对象。
优点:
	1. 用户只需要关心所需产品对应的工厂,无须关心创建细节。
	2. 加入新产品符合开闭原则,提高可扩展型。
缺点:
	1. 类个数过多,增加复杂性。
	2. 增加系统的抽象性和理解程度。

工厂方法工厂就是前面我们简单工厂所说的用于创建对象实例的统一工厂类;方法就是我们把工厂类生产各种对象实例的方法抽离出来,专门放到一个抽象类里面作为并不具体实现的抽象方法,如此,我们需要生产什么类的对象,我们就定义一个专门生产该类对象的工厂类,该类显然应该继承自我们所说的抽象类。

于是我们对于简单工厂的例子场景,可以得到下面的UML图:

工厂方法例UML图

上图,重点关注到:第三层的ITBookFactory,ArtBookFactory,LiteratureBookFactory分别与底层的ITBook,ArtBook,LituratureBook有<< create >>关系。而这三个Factory类都继承自底层的BookFactory,使这三个类重写Book produceBook()这个方法用于生产各自独特的对象。

下面是用例的代码段(Book,ITBook,ArtBook,LiteratureBook类没有在下面列出,具体查看上面):

/*抽象工厂类,并不生产对象。而由具体的子类来生产。
	符合开闭原则:如果新增工厂,只需新增一个类并集成自这个类即可
*/
public abstract class BookFactory {
    public abstract Book produceBook();
}
/***********三个具体工厂类,用于生产不同类的对象******************/
// 专门生产ITBook对象
public class ITBookFactory extends BookFactory {
    @Override
    public Book produceBook() {
        return new ITBook();
    }
}
// 专门生产ArtBook对象
public class ArtBookFactory extends BookFactory {
    @Override
    public Book produceBook() {
        return new ArtBook();
    }
}
// 专门生产LiteratureBook对象
public class LiteratureBookFactory extends BookFactory {
    @Override
    public Book produceBook() {
        return new LiteratureBook();
    }
}
/********************测试类************************/
public class Test {
    public static void main(String[] args) {

        BookFactory itFactory = new ITBookFactory();
        BookFactory artFactory = new ArtBookFactory();
        BookFactory literatureFactory = new LiteratureBookFactory();

        Book book1 = itFactory.produceBook();
        book1.produce();

        Book book2 = artFactory.produceBook();
        book2.produce();

        Book book3 = literatureFactory.produceBook();
        book3.produce();
    }
}

代码很容易理解。在外部(Test类),我们只需要知道抽象工厂类即可,并将抽象工厂类的引用指向具体的工厂类。 并调用相应的方法生产对应工厂即可以生产的对象即可。

抽象工厂

定义:抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口,而无须制定它们具体的类。
类型:创建型
适用场景:
	1. 客户端(应用层)不依赖产品类实例如何被创建,实现等细节;
	2. 强调一系列相关产品对象(同一产品族)一起使用对创建对象需要大量重复的代码
	3. 提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现。
优点:
	1. 具体产品在应用层代码隔离,无须关心创建细节
	2. 将一个系列的产品族统一到一起创建
缺点:
	1. 规定了所有可能被创建的产品集合,产品族中扩展新的产品困难,需要修改抽象工厂的接口。
	2. 增加了系统的抽象性,理解难度。

引入两个概念:产品等级结构产品族。下面是他人的讲解文章,讲解的很好:
产品等级结构与产品族

预设场景:豆瓣读书场景:现有IT,Art两个种类的书籍(Book),并且每种书籍都会有相
关的用户的读后感或者对这本书的评价,我们统称为Article。

开始设计。一个工厂能生产同一产品族的产品:Book,Article。 比如说:ITBookFactory生产ITBook,ITArticle;而ArtBookFactory生产ArtBook,ArtArticle。
其中,ITBook与ArtBook属于同一产品等级结构,ITArticle与ArtArticle亦如此;

图最容易理解了,下面是类关系UML图:
抽象工厂例UML图

从图中我们可以看到Test类只需要与ITBookFactory和ArtBookFactory接触并获取相应的类。而 这两个工厂类都实现BookFactory接口,这个接口定义了两个规范:必须能够生产Book和Article。 从图中的ITBookFactory和ArtBookFactory工厂类可以看到他们分别都能创建 (<< create >>) Article,Book的对象实例。

上代码。

定义同一产品族内,不同产品等级结构中,各不同产品所要实现的与之对应的接口:

/****两个接口(Interface)Book,Article分别定义两个实体的公共动作***/

/****为方便起见,接口的逻辑实现仅仅用一个简单函数来占位**********/

// Book 接口:定义Book的统一动作逻辑
public interface Book {
    void produce();
}
// Article 接口:定义Article的统一动作逻辑
public interface Article {
    void produce();
}

定义实现接口的实体:

/***具体实现接口的实体类****/

/**IT方面的**/
public class ITBook implements Book {
    @Override
    public void produce() {
        System.out.println("Produce a IT book.");
    }
}
public class ITArticle implements Article {
    @Override
    public void produce() {
        System.out.println("Produce a IT Article.");
    }
}

/**Art方面的**/
public class ArtBook implements Book {
    @Override
    public void produce() {
        System.out.println("Produce a art Book.");
    }
}
public class ArtArticle implements Article {
    @Override
    public void produce() {
        System.out.println("Produce a art Article.");
    }
}
/**定义每个工厂都需要实现的接口;
*每个工厂生产的产品属于同一个产品族**/
public interface BookFactory {
    Book produceBook();
    Article produceArticle();
}

定义工厂协议(接口),每个工厂都需要遵循这个协议:每个工厂都能够生产Book,Article,只是不同工厂生产不同类型的Book,Article而已。

/**定义具体的工厂(相当不同的厂商),生产同一产品族(同一厂商)的产品**//**由于这些工厂类(厂商)都遵循同一个协议(实现同一个接口),
*他们都能生产各种相同类型的产品(Book,Article),只是他们生产具体产品的方式不同
*而已(IT工厂生产ITBook,ITArticle,而Art工厂生产ArtBook,ArtArticle。**/

/**属于ITBook工厂(ITBookFactory)生产的产品族有:ITBook,ITArticle**/
public class ITBookFactory implements BookFactory {
    @Override
    public Book produceBook() {
        return new ITBook();
    }

    @Override
    public Article produceArticle() {
        return new ITArticle();
    }
}

/**属于ArtBook工厂(ArtBookFactory)生产的产品族有:ArtBook,ArtArticle**/
public class ArtBookFactory implements BookFactory {
    @Override
    public Book produceBook() {
        return new ArtBook();
    }

    @Override
    public Article produceArticle() {
        return new ArtArticle();
    }
}

下面是测试类:

import java.util.Random;
/**
 * 两种方法进行测试:
 *  1. 普通测试。为了把两个类都覆盖到,是用if判断传参。
 *  2. 反射机制。关键点:Class.forName(String className).newInstace();
 */
public class Test {
    public static void main(String[] args) {

        // 为了把两个类都测试到,我们新定义个两种test方法。
        // 根据传入的参数不同进行不同类型的测试

        // 5次,随机对一种测试方法进行测试
        for(int i = 0 ; i < 5 ; i ++ ) {

            double rand = new Random().nextDouble();

            System.out.println( "================第"+ (i+1) + "次随机测试" + rand + "================\n" );

            if ( rand  < 0.5) {
                test("IT");
                test("Art");
            } else {
                test(ITBookFactory.class);
                test(ArtBookFactory.class);
            }

            System.out.println( "================第"+ i + "次随机测试END=================================\n" );
        }

    }

    private static void test(String type){
        System.out.println("Calling \"void test(String type);\" ");

        BookFactory bookFactory = null;

        if( "IT".equalsIgnoreCase(type) ){
            bookFactory = new ITBookFactory();
        } else if( "Art".equalsIgnoreCase(type) ){
            bookFactory = new ArtBookFactory();
        } else {
            return;
        }

        Book book = bookFactory.produceBook();
        Article article = bookFactory.produceArticle();

        book.produce();
        article.produce();

        System.out.println();

    }

    private static void test(Class c){
        System.out.println("Calling \"void test(Class c);\" ");

        BookFactory bookFactory = null;

        try {
            bookFactory = (BookFactory) Class.forName(c.getName()).newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        Book book = bookFactory.produceBook();
        Article article = bookFactory.produceArticle();

        book.produce();
        article.produce();

        System.out.println();
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
简单工厂工厂方法抽象方法都是与工厂相关设计模式简单工厂模式(Simple Factory Pattern)是一种创建模式,它提供一个简单工厂创建所需的对象,而无需直接实例化对象。简单工厂模式的核心是一个工厂,包含了必要的创建逻辑,根据给定的条件来创建对象。简单工厂模式适合创建的对象较少且不需要频繁扩展的情况。 工厂方法模式(Factory Method Pattern)是一种创建模式,它定义一个用于创建对象的接口,让子决定实例化哪个工厂方法模式将实例化操作延迟到子类中进行,符合开闭原则。工厂方法模式适用于创建的对象需要扩展的情况。 抽象工厂模式(Abstract Factory Pattern)是一种创建模式,它提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体抽象工厂模式提供了一个创建一系列相关或相互依赖的对象的接口,而无需指定它们的具体抽象工厂模式适用于需要一次性创建一整套相关或相互依赖的对象的情况。 抽象方法模式(Abstract Method Pattern)是一种行为模式,它定义了一个用于创建对象的接口,由子决定实例化哪个抽象方法模式在工厂方法模式的基础上将工厂抽象化,工厂类中工厂方法也变成了抽象方法,由子来实现。抽象方法模式适用于需要扩展工厂的情况。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值