02.设计模式-工厂方法模式

简介: 安卓源码中比较典型的工厂方法模式,Iterator,Iterator是一个接口,它定义了迭代器的必须的一些规则,也就是说可以使用迭代器遍历元素的集合都是实现了这个接口,比如ArrayList,HashSet都是Collection的实现类,而Coll...

安卓源码中比较典型的工厂方法模式,Iterator,Iterator是一个接口,它定义了
迭代器的必须的一些规则,也就是说可以使用迭代器遍历元素的集合都是实现了这个接口,比如ArrayList,HashSet都是Collection的实现类,而Colloction这个接口继承了
Iterable接口,这样就使每一个集合类中都可以使用Iterator<T> iterator()的方式来遍历集合

public interface Iterable<T> {
    /**
     * Returns an iterator over elements of type {@code T}.
     *
     * @return an Iterator.
     */
    Iterator<T> iterator();

    /**
     * Performs the given action for each element of the {@code Iterable}
     * until all elements have been processed or the action throws an
     * exception.  Unless otherwise specified by the implementing class,
     * actions are performed in the order of iteration (if an iteration order
     * is specified).  Exceptions thrown by the action are relayed to the
     * caller.
     *
     * @implSpec
     * <p>The default implementation behaves as if:
     * <pre>{@code
     *     for (T t : this)
     *         action.accept(t);
     * }</pre>
     *
     * @param action The action to be performed for each element
     * @throws NullPointerException if the specified action is null
     * @since 1.8
     */
    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }

    /**
     * Creates a {@link Spliterator} over the elements described by this
     * {@code Iterable}.
     *
     * @implSpec
     * The default implementation creates an
     * <em><a href="Spliterator.html#binding">early-binding</a></em>
     * spliterator from the iterable's {@code Iterator}.  The spliterator
     * inherits the <em>fail-fast</em> properties of the iterable's iterator.
     *
     * @implNote
     * The default implementation should usually be overridden.  The
     * spliterator returned by the default implementation has poor splitting
     * capabilities, is unsized, and does not report any spliterator
     * characteristics. Implementing classes can nearly always provide a
     * better implementation.
     *
     * @return a {@code Spliterator} over the elements described by this
     * {@code Iterable}.
     * @since 1.8
     */
    default Spliterator<T> spliterator() {
        return Spliterators.spliteratorUnknownSize(iterator(), 0);
    }
}
public interface Iterator<E> {
    /**
     * Returns true if there is at least one more element, false otherwise.
     * @see #next
     */
    public boolean hasNext();

    /**
     * Returns the next object and advances the iterator.
     *
     * @return the next object.
     * @throws NoSuchElementException
     *             if there are no more elements.
     * @see #hasNext
     */
    public E next();

    /**
     * Removes the last object returned by {@code next} from the collection.
     * This method can only be called once between each call to {@code next}.
     *
     * @throws UnsupportedOperationException
     *             if removing is not supported by the collection being
     *             iterated.
     * @throws IllegalStateException
     *             if {@code next} has not been called, or {@code remove} has
     *             already been called after the last call to {@code next}.
     */
    public void remove();
}

至于说为什么要这样做,为什么定义一个统一的规则而不是单独的在每个集合类中去实现?因为既然每个集合都需要遍历元素,不如定义一个统一的接口去规范,强制要求子类去实现,但每种集合遍历的方式有所不同,所以具体的遍历方式就要由子类去实现,这样做,即简化了代码量,又方便了拓展,只需要实现上层规则即可,不用去修改上层的东西,相对于简单工厂模式,灵活性更高

接下来我们来探索工厂方法模式产生的条件

举个例子,开发过程中缓存是经常用到的功能,它有两个最基本的方法,写缓存和读缓存,缓存的方式又有多种,比如最开始我们的需求是将缓存数据写入数据库,于是有了这个类

public class CacheDbOperator {
    
    private CacheDb cacheDb;

    public CacheDbOperator() {
        cacheDb = new CacheDb();
    }
    
    public void save(){
        cacheDb.save();
    }
    
    public void read(){
        cacheDb.read();
    }
}

public class CacheDb {
    public void save(){
        System.out.println("保存到db");
    }
    
    public void read(){
        System.out.println("从db读取");
    }
}

那么我们只需要在需要写入的地方写入缓存,在需要读取的地方读取缓存就行了

public static void main(String[] args) {
        CacheDbOperator dbOperator = new CacheDbOperator();
        //写入缓存
        dbOperator.save();
        //读取缓存
        dbOperator.read();
    }

然后第二天,需求改变,缓存不再使用数据库,而是使用安卓种很常用的SharePreference,那么另一个缓存类应运而生

public class CacheSpOperator {
    
    private CacheSp cacheSp;

    public CacheSpOperator() {
        cacheSp = new CacheSp();
    }
    
    public void save(){
        cacheSp.save();
    }
    
    public void read(){
        cacheSp.read();
    }
}

public class CacheSp {
    public void save(){
        System.out.println("保存到sp");
    }
    
    public void read(){
        System.out.println("从sp读取");
    }
}

同样在需要的地方去调用它

public static void main(String[] args) {
        //CacheDbOperator dbOperator = new CacheDbOperator();
        //dbOperator.save();
        //dbOperator.read();

        CacheSpOperator spOperator = new CacheSpOperator();
        spOperator.save();
        spOperator.read();
    }

我们知道需求是万变的,第三天SharePreference的缓存方式被抛弃了,开始采用最差的缓存,保存数据到本地文件中,然后...

public class CacheFileOperator {
    
    private CacheFile cacheFile;

    public CacheFileOperator() {
        cacheFile = new CacheFile();
    }
    
    public void save(){
        cacheFile.save();
    }
    
    public void read(){
        cacheFile.read();
    }
}
public class CacheFile {
    public void save(){
        System.out.println("保存到file");
    }
    
    public void read(){
        System.out.println("从file读取");
    }
}

然后再调用

public static void main(String[] args) {
        //CacheDbOperator dbOperator = new CacheDbOperator();
        //dbOperator.save();
        //dbOperator.read();

        //CacheSpOperator spOperator = new CacheSpOperator();
        //spOperator.save();
        //spOperator.read();

        CacheFileOperator fileOperator = new CacheFileOperator();
        fileOperator.save();
        fileOperator.read();    
    }

这样三个操作下来,仔细思考一下,其实这三种缓存方式虽然操作的对象不同,但是它们有一些共性,都具有save和read的方法,结合上边我们分析的Iterator迭代器的源码发现,其实这两个方法是可以抽取出来,作为一个必须实现的规则的,那么我们创建出这个封装共性的接口

public interface Operator {
    void save();
    void read();
}

我们将所有缓存类的共性抽取到一个接口中,然后缓存类实现这个接口即可

public class CacheDb implements Operator{
    
    @Override
    public void save(){
        System.out.println("保存到db");
    }

    @Override
    public void read(){
        System.out.println("从db读取");
    }
    
}

public class CacheFile implements Operator{
    
    @Override
    public void save(){
        System.out.println("保存到file");
    }
    
    @Override
    public void read(){
        System.out.println("从file读取");
    }
}

public class CacheSp implements Operator{
    
    @Override
    public void save(){
        System.out.println("保存到sp");
    }
    
    @Override
    public void read(){
        System.out.println("从sp读取");
    }
}

这样一来,缓存类的规范就产生了,当我们未来还有新的缓存方式要使用的时候,只需要实现这个接口,定义自己的实现代码即可

我们再深入想想,其实可以发现,每一个缓存操作类在使用的时候是不是也有一些共性

CacheDbOperator dbOperator = new CacheDbOperator();
dbOperator.save();
dbOperator.read();

CacheFileOperator fileOperator = new CacheFileOperator();
fileOperator.save();
fileOperator.read();
        
CacheSpOperator spOperator = new CacheSpOperator();
spOperator.save();
spOperator.read();

它们每一个的操作是相似的,都是创建对象,然后调用统一的方法读取或者写入,其实这三种操作缓存的方式可以看作缓存操作的具体实现方式,而具体实现方式中又存在共性,那么我们可否创建一个抽象工厂来管理这些具体的实现类呢

我们要实现的效果就是,我创建一个抽象工厂,然后我每次在需要缓存的地方使用我的抽象工厂,而不用涉及每一个具体的实现。因为我们知道涉及到需要缓存的地方很多,一个项目中可能会有成百上千个位置要设置,那么如果有一天我们要切换缓存类,那岂不是成百上千个地方的代码需要修改,我们在需要缓存的位置使用抽象工厂,然后当需要切换的时候只要在抽象工厂中做一点改动就达到切换引擎的目的,这样要如何实现?

我们发现其实每一个缓存类除了创建对象的操作不同之外,save和read方法都是一致的,那么我们完全可以将创建对象的操作设置为抽象方法由每一个字类去实现,但是具体的调用方法抽取到抽象类中,如下

public abstract class CacheFactory implements Operator{
    
    public abstract Operator newCacheOperator();

    @Override
    public void save() {
        newCacheOperator().save();
    }

    @Override
    public void read() {
        newCacheOperator().read();
    }

    
}

让每一个缓存类继承这个抽象类,实现抽象方法创建对象

public class CacheDbOperator extends CacheFactory{

    @Override
    public Operator newCacheOperator() {
        return new CacheDb();
    }
}

public class CacheFileOperator extends CacheFactory{

    @Override
    public Operator newCacheOperator() {
        return new CacheFile();
    }
}

public class CacheSpOperator extends CacheFactory{
    
    @Override
    public Operator newCacheOperator() {
        return new CacheSp();
    }
}

这样设计的好处就是顶层的抽象类是永远不需要改变的(假设实现方法覆盖了所有的方面),即便是功能进行了扩展,又采用了很多的缓存实现方式,都只需要让它继承抽象类,实现自己具体的业务逻辑即可,而且,你会发现,切换一个缓存引擎室如此的方便,如下,我们在使用缓存的位置调用的是抽象类,Cache是自己创建的一个继承了抽象类CacheFactory的一个字类

public static void main(String[] args) {
        Cache cache =  new Cache();
        cache.save();
        cache.read();
    }

也就是说只要是设置缓存的位置或者读取缓存的位置,我们都这样调用,完全没有涉及到具体的实现,假设现在我们使用的数据库缓存,我们只要单独设置一个类即可,如下

public class Cache extends CacheFactory{

        @Override
        public Operator newCacheOperator() {
            
            return new CacheDb();
        }
        
    }

然后有一天我们需要将数据库缓存切换到SharePreference缓存,那么只需修改一行代码

public class Cache extends CacheFactory{
    @Override
    public Operator newCacheOperator() {
        
        return new CacheSp();
    }
}

然后所有的缓存都已经被切换过来了,省了好几百行上千行的代码修改量,这就是工厂方法模式的优点

相关文章
|
1月前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
|
3月前
|
设计模式 数据库连接 PHP
PHP中的设计模式:提升代码的可维护性与扩展性在软件开发过程中,设计模式是开发者们经常用到的工具之一。它们提供了经过验证的解决方案,可以帮助我们解决常见的软件设计问题。本文将介绍PHP中常用的设计模式,以及如何利用这些模式来提高代码的可维护性和扩展性。我们将从基础的设计模式入手,逐步深入到更复杂的应用场景。通过实际案例分析,读者可以更好地理解如何在PHP开发中应用这些设计模式,从而写出更加高效、灵活和易于维护的代码。
本文探讨了PHP中常用的设计模式及其在实际项目中的应用。内容涵盖设计模式的基本概念、分类和具体使用场景,重点介绍了单例模式、工厂模式和观察者模式等常见模式。通过具体的代码示例,展示了如何在PHP项目中有效利用设计模式来提升代码的可维护性和扩展性。文章还讨论了设计模式的选择原则和注意事项,帮助开发者在不同情境下做出最佳决策。
|
1月前
|
设计模式 开发者 Python
Python编程中的设计模式:工厂方法模式###
本文深入浅出地探讨了Python编程中的一种重要设计模式——工厂方法模式。通过具体案例和代码示例,我们将了解工厂方法模式的定义、应用场景、实现步骤以及其优势与潜在缺点。无论你是Python新手还是有经验的开发者,都能从本文中获得关于如何在实际项目中有效应用工厂方法模式的启发。 ###
|
1月前
|
设计模式 安全 Java
Kotlin - 改良设计模式 - 构建者模式
Kotlin - 改良设计模式 - 构建者模式
|
1月前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
42 1
|
2月前
|
设计模式 Java Kotlin
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
本教程详细讲解Kotlin语法,适合希望深入了解Kotlin的开发者。对于快速学习Kotlin语法,推荐查看“简洁”系列教程。本文重点介绍了构建者模式在Kotlin中的应用与改良,包括如何使用具名可选参数简化复杂对象的创建过程,以及如何在初始化代码块中对参数进行约束和校验。
32 3
|
3月前
|
设计模式 算法 安全
设计模式——模板模式
模板方法模式、钩子方法、Spring源码AbstractApplicationContext类用到的模板方法
设计模式——模板模式
|
3月前
|
设计模式 数据库连接 PHP
PHP中的设计模式:如何提高代码的可维护性与扩展性在软件开发领域,PHP 是一种广泛使用的服务器端脚本语言。随着项目规模的扩大和复杂性的增加,保持代码的可维护性和可扩展性变得越来越重要。本文将探讨 PHP 中的设计模式,并通过实例展示如何应用这些模式来提高代码质量。
设计模式是经过验证的解决软件设计问题的方法。它们不是具体的代码,而是一种编码和设计经验的总结。在PHP开发中,合理地使用设计模式可以显著提高代码的可维护性、复用性和扩展性。本文将介绍几种常见的设计模式,包括单例模式、工厂模式和观察者模式,并通过具体的例子展示如何在PHP项目中应用这些模式。
|
3月前
|
设计模式 Java Spring
spring源码设计模式分析-代理设计模式(二)
spring源码设计模式分析-代理设计模式(二)
|
3月前
|
设计模式
设计模式-工厂模式 Factory Pattern(简单工厂、工厂方法、抽象工厂)
这篇文章详细解释了工厂模式,包括简单工厂、工厂方法和抽象工厂三种类型。每种模式都通过代码示例展示了其应用场景和实现方法,并比较了它们之间的差异。简单工厂模式通过一个工厂类来创建各种产品;工厂方法模式通过定义一个创建对象的接口,由子类决定实例化哪个类;抽象工厂模式提供一个创建相关或依赖对象家族的接口,而不需要明确指定具体类。
设计模式-工厂模式 Factory Pattern(简单工厂、工厂方法、抽象工厂)