安卓源码中比较典型的工厂方法模式,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();
}
}
然后所有的缓存都已经被切换过来了,省了好几百行上千行的代码修改量,这就是工厂方法模式的优点