java编程思想第四版第九章总结

简介: 本章非常重要, 里面的内容涉及到了三个设计模式, 以及接口的有点,掌握这些就是掌握了重点

1. 策略设计模式




  • 先简单了解一下:微信图片_20220507172020.png微信图片_20220507172032.png

 和模板方法模式的区别:

微信图片_20220507172124.png文章里还有一个例子:微信图片_20220507172205.png微信图片_20220507172220.png


备注:我来分解,解释一下这个例子。


    将共同的方法定义成了一个接口,在这个接口中并没有这个共同方法的实现。


    在Strategy类中,定义了一个方法execute,它的参数是拥有共同方法的接口类。

    用户Context在调用Strategy的execute方法时,在定义这个共同的方法。 这样就大大提高了灵活性。因为公共方法也是可以后定义的。而不是在创建类的时候就定义好了。

   

 下面把这个例子敲成代码


package net.mindview.interfaces;
//公共方法回调类
interface SameCallback{
    void doTheSame();
}
//定义了一个策略类
interface Strategy {
    void execute(SameCallback sc);
}
//定义策略实现类
class concreteStrategy1 implements Strategy {
    @Override
    public void execute(SameCallback sc) {
        sc.doTheSame();
    }
}
class concreteStrategy2 implements Strategy {
    @Override
    public void execute(SameCallback sc) {
        sc.doTheSame();
    }
}
class concreteStrategy3 implements Strategy {
    @Override
    public void execute(SameCallback sc) {
        sc.doTheSame();
    }
}
public class Context {
    public static void main(String[] args) {
        Strategy strategy = new concreteStrategy1();
        //这个公共方法类
        SameCallback sc = new SameCallback() {
            @Override
            public void doTheSame() {
                System.out.println("do same things");
            }
        };
        //那个策略需要用到公共方法类,调用即可,如果不用, 那就不在方法中小勇
        strategy.execute(sc);
        strategy = new concreteStrategy2();
        strategy.execute(sc);
    }
}

微信图片_20220507172310.png微信图片_20220507172330.png

2. 解耦



  • 看下面这两个例子:


案例一
package net.mindview.interfaces.classprocessor;
import java.math.BigInteger;
import java.util.Arrays;
/**
 * 处理器
 */
class Processor {
    public String name(){
        return getClass().getName();
    };
    //处理
    Object process(Object input) {
        return input;
    }
}
/**
 * 大写处理器
 */
class UpCase extends Processor {
    @Override
    public
    Object process(Object input) {
        return ((String)input).toUpperCase();
    }
}
/**
 * 小写处理器
 */
class DownCase extends Processor {
    @Override
    public
    Object process(Object input) {
        return ((String)input).toLowerCase();
    }
}
/**
 * 分割数组处理器
 */
class Splitter extends Processor {
    @Override
    public
    Object process(Object input) {
        return Arrays.toString(((String)input).toString().split(" "));
    }
}
public class Apply {
    public static void process(Processor p, Object s){
        System.out.println("Using Processor "+ p.name());
        System.out.println(p.process(s));
    }
    public static String s = "Disagreement with beliefs is by definition incorrect";
    /**
     * 向本例这样, 创建一个能够根据所传递的参数对象的不同而具有不同行为的方法,
     * 被称为策略设计模式
     * @param args
     */
    public static void main(String[] args) {
        BigInteger b = new BigInteger("111111111111111");
        process(new UpCase(), s);
        process(new DownCase(), s);
        process(new Splitter(), s);
    }
    /**
     * 这个类使用到了一个设计模式: 叫做策略设计模式
     * 所谓 的策略设计模式指的是: 能够根据传递参数的不同而具有不同的行为的方法, 被称为策略设计模式
     * 这类方法包含了所有执行的算法中固定不变的部分(这里是基类Processor,Object),而策略就是实际调用该方法时传递的实际参数,
     * 这部分参数是变化的,因此被称之为是"策略"部分.
     * 在main中看到了三种不同类型的策略应用到了String类型s对象上。
     */
}


在这段代码中,定义了一个处理器,这个处理器有两个方法name()和process(). 然后定义了三个类, UpCase, DownCase,Spltter继承自这个类. 当我们在Apply类中定义


process(Process process, Object o)方法, 方法传递的参数是父类Process. 这样不仅可以处理父类,还可以处理子类. 在main方法中就有体现. 这就是策略设计模式的思想。但是,他还有局限性。如果在main中想调用Apply.process()方法, 则必须传递Process类或者子类,不能传递其他类。 下面这个案例滤波器就说明了这一点:


案例二
package net.mindview.interfaces.filters;
//波形
public class Waveform {
    //计数器,第几波
    private static long counter;
    private final long id = counter ++;
    //当前第几波
    @Override
    public String toString() {
        return "Waveform " + id; 
    }
}
package net.mindview.interfaces.filters;
//滤波器
public class Filter {
    public String name(){
        return getClass().getSimpleName();
    }
    //加工
    public Waveform process(Waveform input){
        return input;
    }
}
package net.mindview.interfaces.filters;
/**
 * 低频电磁波
 */
public class LowPass extends Filter{
    //切波的波长
    double cutoff;
    public LowPass(double cutoff){
        this.cutoff = cutoff;
    }
    //加工
    @Override
    public Waveform process(Waveform input) {
        return input;
    }
}
package net.mindview.interfaces.filters;
/**
 * 高频电磁波
 */
public class HighPass extends Filter {
    //切波
    double cutoff;
    public HighPass(double cutoff){
        this.cutoff = cutoff;
    }
    //加工
    @Override
    public Waveform process(Waveform input) {
        return input;
    }
}
package net.mindview.interfaces.filters;
/**
 * 段波电磁波
 */
class BandPass extends Filter {
    //其实切波点, 最高且波点
    double cutoff ,highCutoff;
    public BandPass(double cutoff, double highCutoff){
        this.cutoff = cutoff;
        this.highCutoff = highCutoff;
    }
    //加工
    @Override
    public Waveform process(Waveform input) {
        return input;
    }
}


在上面这个类中,我们定义了一个Filter滤波器。这里面有何Process中同名的两个方法。这个时候,我想复用案例一的Apply类中的process()方法,可不可以呢?不可以。因为Filter不是Process的子类。虽然, process中的方法体调用完全适用于继承Filter类的子类。为什么不可以呢?Apply就是一个应用类。应用类要处理一些列的类。他可以堆字符串进行处理, 应该也可以对波进行处理呀。现在不可以,因为Process类是一个实体类,他和Apply类中的process方法紧密耦合。如果能够实现解耦,那么,就可以将Apply类更加泛化。这就是将Process作为接口来处理


案例三
package net.mindview.interfaces.classprocessor2;
import java.math.BigInteger;
import java.util.Arrays;
/**
 * 处理器
 */
interface Processor {
    public String name();
    //处理
    Object process(Object input);
}
/**
 * 处理器有一部分是所有处理器都是一样的.有一部分是不同的.
 * 所以定义一个抽象类, 将共有方法定义为实体方法, 将各自实现的部分定义为抽象的
 */
abstract class StringProcess implements Processor {
    @Override
    public String name() {
        return getClass().getSimpleName();
    }
    public abstract Object process(Object input);
}
/**
 * 大写处理器
 */
class UpCase extends StringProcess {
    @Override
    public
    Object process(Object input) {
        return ((String)input).toUpperCase();
    }
}
/**
 * 小写处理器
 */
class DownCase extends StringProcess {
    @Override
    public
    Object process(Object input) {
        return ((String)input).toLowerCase();
    }
}
/**
 * 分割数组处理器
 */
class Splitter extends StringProcess {
    @Override
    public
    Object process(Object input) {
        return Arrays.toString(((String)input).toString().split(" "));
    }
}
public class Apply {
    public static void process(Processor p, Object s){
        System.out.println("Using Processor "+ p.name());
        System.out.println(p.process(s));
    }
    public static String s = "Disagreement with beliefs is by definition incorrect";
    /**
     * 向本例这样, 创建一个能够根据所传递的参数对象的不同而具有不同行为的方法,
     * 被称为策略设计模式
     * @param args
     */
    public static void main(String[] args) {
        BigInteger b = new BigInteger("111111111111111");
        process(new UpCase(), s);
        process(new DownCase(), s);
        process(new Splitter(), s);
    }
    /**
     * 这个类使用到了一个设计模式: 叫做策略设计模式
     * 所谓 的策略设计模式指的是: 能够根据传递参数的不同而具有不同的行为的方法, 被称为策略设计模式
     * 这类方法包含了所有执行的算法中固定不变的部分(这里是基类Processor,Object),而策略就是实际调用该方法时传递的实际参数,
     * 这部分参数是变化的,因此被称之为是"策略"部分.
     * 在main中看到了三种不同类型的策略应用到了String类型s对象上。
     */
}


  • 在这个类里面,将Process定义为了接口。并且添加了StringProcess 字符串处理器类,提供了字符串处理的公用方法。这时如何修改案例二,才能让Apply成为一个公用类呢?其中一个方法是让Filter实现Process接口。但为题又来了,Filter中的process的输入输出参数都是Waveform类型的对象。而Process中process的输入输出都是Object。这时我们使用一个适配器来解决这个问题

 

package net.mindview.interfaces.filters2;
import net.mindview.interfaces.classprocessor2.Processor;
/**
 * 由于Filter中的process的输入输出参数都是Waveform,而Processor中process的输入
 * 输出参数都是Object. 因此,写一个适配器,让Filter可以适配Processor类型的接口
 * 
 */
public class FilterAdapter implements Processor{
    Filter filter;
    public FilterAdapter(Filter filter){
        this.filter = filter;
    }
    @Override
    public String name() {
        return filter.name();
    }
    @Override
    public Object process(Object input) {
        return filter.process((Waveform)input);
    }
}


接下来看看Apply类如何处理的?


public class Apply {
    public static void process(Processor p, Object s){
        System.out.println("Using Processor "+ p.name());
        System.out.println(p.process(s));
    }
    public static String s = "Disagreement with beliefs is by definition incorrect";
    /**
     * 向本例这样, 创建一个能够根据所传递的参数对象的不同而具有不同行为的方法,
     * 被称为策略设计模式
     * @param args
     */
    public static void main(String[] args) {
        System.out.println("======处理字符串===========");
        process(new UpCase(), s);
        process(new DownCase(), s);
        process(new Splitter(), s);
        System.out.println("======过滤波===========");
        process(new FilterAdapter(new LowPass(1.0)), new Waveform());
        process(new FilterAdapter(new HighPass(100.0)), new Waveform());
        process(new FilterAdapter(new BandPass(1.0,10.0)), new Waveform());
    }
}


上面这个例子就示范了使用接口的好处,让类和方法解耦。同时还涉及到一个设计模式,

叫适配器设计模式。适配器设计模式有三种, 确切的说这是对象适配器模式.


3. 适配器设计模式



  参考文章:http://blog.csdn.net/zxt0601/article/details/52848004


  定义:适配器模式将某个类的接口转换成客户端期望的另一个接口表示,主的目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作。其别名为包装器(Wrapper)。


  属于结构型模式


    主要分为三类:类适配器模式、对象的适配器模式、接口的适配器模式。

  本文做以下约定


  • 需要被适配的类,对象,接口被定义为源(目前已有的),简称为src(source).


  • 最终需要输出的(我们需要的),简称dst(destination,也叫target)


  • 适配器Adapter


    一句话描述适配器模式:src->adapter->dist,即src以某种形式(类,对象,接口)给到适配器,适配器最终输出dist。


  使用 场景


  • 想要使用一个已经存在的类,但如果它的方法不满足需求时;


  • 两个类的职责相同或相似,但是具有不同的接口时要使用它;


  • 应该在双方都不太容易修改的时候再使用适配器模式适配,而不是一有不同时就使用它


   第一类: 类适配器模式


    一句话描述使用方法:Adapter类,通过继承src类,实现dist接口,完成src -> dist的适配.


    以充电器为例:充电器本身就是一个适配器。输入的时220v电压, 输出的是5v电压。


    首先, 有一个src类,本身是输出220v电压 

 

package net.mindview.interfaces.adaptor;
/**
 * 这是目前有的电压 200v
 * @author samsung
 *
 */
public class Voltage220 {
    public int output(){
        int i = 220;
        System.out.println("这是220v电压");
        return i;
    }
}


然后, 有一个目标使用电压是5v


package net.mindview.interfaces.adaptor;
/**
 * 我们需要使用的目标电压是5v
 * 这是一个5v电压的接口
 * @author samsung
 *
 */
public interface Voltage5 {
    public int output();
}


定义一个适配器, 将220v电压经过处理后转换为5v电压供用户使用


package net.mindview.interfaces.adaptor;
/**
 * 适配器,将220v电压输出为5v电压
 * 适配器 继承src类, 实现dist类,实现从src->dist的转换
 */
public class VoltageAdapter extends Voltage220 implements Voltage5{
    @Override
    public int output() {
        int src = super.output();
        System.out.println("适配器工作开始适配电压");
        int dist = src/44;
        System.out.println("适配完成后输出电压:" + dist);
        return dist;
    }
}


 有一款手机需要充电,充电电压是5v


package net.mindview.interfaces.adaptor;
public class Mobile {
    public void chargeing(Voltage5 v5){
        if(v5.output() == 5){
            System.out.println("正常充电");
        } else {
            System.out.println("秒变note7");
        }
    }
}


使用手机的时候,需要将家里的220v电压转换为5v输出.调用适配器


public static void main(String[] args) {
        Mobile m = new Mobile();
        m.chargeing(new VoltageAdapter());
    }


输出:


===============类适配器==============
我是220V
适配器工作开始适配电压
适配完成后输出电压:5
电压刚刚好5V,开始充电

微信图片_20220507172803.png

小结:


Java这种单继承的机制,所有需要继承的我个人都不太喜欢。


所以类适配器需要继承src类这一点算是一个缺点,


因为这要求dst必须是接口,有一定局限性;


且src类的方法在Adapter中都会暴露出来,也增加了使用的成本。


但同样由于其继承了src类,所以它可以根据需求重写src类的方法,使得Adapter的灵活性增强了。


    第二类: 对象适配器模式


    基本思路和类适配器一样, 只是将adapter类做了修改, 这次不继承src类,而是持有src类实例对象,以解决兼容性问题。


    一句话总结使用方法:持有src类,实现dist类接口, 实现src -> dist的适配  


    (根据"合成复用规则", 在系统中尽量使用组合来代替继承)


package net.mindview.interfaces.adaptor;
/**
 * 持有src类,实现dist类接口, 实现src -> dist的适配
 * @author samsung
 *
 */
public class VoltageAdapter2 implements Voltage5{
    Voltage220 voltage220;
    public VoltageAdapter2(Voltage220 voltage220){
        this.voltage220 = voltage220;
    }
    @Override
    public int output() {
        System.out.println("得到220v电压");
        int src = voltage220.output();
        System.out.println("经过处理,输出5v电压");
        int dist = src/44;
        System.out.println("最终使用的电压是"+dist+"v");
        return dist;
    }
}


测试结果:


得到220v电压
这是220v电压
经过处理,输出5v电压
最终使用的电压是5v
正常充电

微信图片_20220507172916.png


小结:



对象适配器和类适配器其实算是同一种思想,只不过实现方式不同。


根据合成复用原则,组合大于继承,


所以它解决了类适配器必须继承src的局限性问题,也不再强求dst必须是接口。


同样的它使用成本更低,更灵活。

 

  第三类 接口适配器模式

    

也有文献称之为认适配器模式(Default Adapter Pattern)或缺省适配器模式。
定义:


  当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求,它适用于一个接口不想使用其所有的方法的情况。


我们直接进入大家最喜爱的源码撑腰环节:


源码撑腰环节:



Android中的属性动画ValueAnimator类可以通过addListener(AnimatorListener listener)方法添加监听器,


那么常规写法如下:


ValueAnimator valueAnimator = ValueAnimator.ofInt(0,100);
        valueAnimator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {
            }
            @Override
            public void onAnimationEnd(Animator animation) {
            }
            @Override
            public void onAnimationCancel(Animator animation) {
            }
            @Override
            public void onAnimationRepeat(Animator animation) {
            }
        });
        valueAnimator.start();


有时候我们不想实现Animator.AnimatorListener接口的全部方法,我们只想监听onAnimationStart,我们会如下写:


ValueAnimator valueAnimator = ValueAnimator.ofInt(0,100);
        valueAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation) {
                //xxxx具体实现
            }
        });
        valueAnimator.start();


显然,这个AnimatorListenerAdapter类,就是一个接口适配器。
查看该Adapter类源码:


public abstract class AnimatorListenerAdapter implements Animator.AnimatorListener,
        Animator.AnimatorPauseListener {
    @Override
    public void onAnimationCancel(Animator animation) {
    }
    @Override
    public void onAnimationEnd(Animator animation) {
    }
    @Override
    public void onAnimationRepeat(Animator animation) {
    }
    @Override
    public void onAnimationStart(Animator animation) {
    }
    @Override
    public void onAnimationPause(Animator animation) {
    }
    @Override
    public void onAnimationResume(Animator animation) {
    }
}


可见,它空实现了Animator.AnimatorListener类(src)的所有方法.
对应的src类:


 public static interface AnimatorListener {
        void onAnimationStart(Animator animation);
        void onAnimationEnd(Animator animation);
        void onAnimationCancel(Animator animation);
        void onAnimationRepeat(Animator animation);
    }


类图:


微信图片_20220507173154.png


我们程序里的匿名内部类就是Listener1 2 这种具体实现类。


new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation) {
                //xxxx具体实现
            }
        }


接口适配器模式很好理解,令我们的程序更加简洁明了。


总结



我个人理解,三种命名方式,是根据 src是以怎样的形式给到Adapter(在Adapter里的形式)来命名的。


类适配器,以类给到,在Adapter里,就是将src当做类,继承,


对象适配器,以对象给到,在Adapter里,将src作为一个对象,持有。


接口适配器,以接口给到,在Adapter里,将src作为一个接口,实现。


Adapter模式最大的作用还是将原本不兼容的接口融合在一起工作。


但是在实际开发中,实现起来不拘泥于本文介绍的三种经典形式,


例如Android中ListView、GridView的适配器Adapter,就不是以上三种经典形式之一,
我个人理解其属于对象适配器模式,一般日常使用中,我们都是在Adapter里持有datas,然后通过getView()/onCreateViewHolder()方法向ListView/RecyclerView提供View/ViewHolder。


Client是Lv Gv Rv ,它们是显示View的类。


所以dst(Target)是View。


一般来说我们有的src是数据datas,
即,我们希望:datas(src)->Adapter->View(dst)->Rv(Client)。


4. 适配接口


  适配接口这一节,主要是再次强调了接口的好处. 可以使用策略设计模式, 在方法实际参数中调用接口, 用户使用的时候就可以根据实际情况传递实现了该接口的实现类. 灵活性更高. 同时, 课本中举了一个例子,Scanner的read方法中调用的就是readable接口。如果我想使用Scanner类(这时java类库中的类),有两种方式。


  • 方式一:新类实现readable接口即可


  • 方式二:如果类已定义好, 不方便修改, 可以使用适配器模式,让已写好的类适配Scanner类。


    针对这两个方法,我们来看案例:


    案例一(对应的方式一)


package net.mindview.interfaces;
import java.io.IOException;
import java.nio.CharBuffer;
import java.util.Random;
import java.util.Scanner;
//随机单次
public class RandomWords implements Readable{
    //首先生成一个全局的随机数.
    //特殊说明:创建一个Random对象的时候可以给定任意一个合法的种子数,种子数只是随机算法的起源数字,和生成的随机数的区
    //间没有任何关系。其中47就是种子,那么为什么是作者要选择47作为种子呢? 原因如下:
    //由47做种后,产生的随机数更加体现了随机性。它没有什么具体的意义,只要理解随机数如果有一个种子,
    //那么会出现比较随机的随机数,而当种子是47的时候,随机率是最大的。
    private static Random rand = new Random(46);
    //定义首字母,字母的范围已固定,不会改变,故定义为static final
    private static final char[] captials = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
    //小写字母
    private static final char[] lowers = "abcdefghijklmnopqrstuvwxyz".toCharArray();
    //元音字母
    private static final char[] vowels = "aeiou".toCharArray();
    private int count;
    //计数,一共生成几个随机单词
    public RandomWords(int count){
        this.count = count;
    }
    @Override
    public int read(CharBuffer cb) throws IOException {
        if(count-- == 0) {
            return -1;
        } 
        //生成一个大写字母
        cb.append(captials[rand.nextInt(captials.length)]);
        //生成一个原因一个辅音
        for(int i=0; i<4; i++){
            cb.append(vowels[rand.nextInt(vowels.length)]);
            cb.append(lowers[rand.nextInt(lowers.length)]);
        }
        cb.append(" ");
        return 10;//返回字符数
    }
    public static void main(String[] args) {
        Scanner s = new Scanner(new RandomWords(4));
        while(s.hasNext()){
            System.out.println(s.next());
        }
    }
}


案例二(对应方式二)


  现在有一个写好的了,随机生成浮点数


package net.mindview.interfaces;
import java.util.Random;
//随机生成浮点数
public class RandomDoubles {
    private static Random rand = new Random(47);
    public double next(){
        return rand.nextDouble();
    }
    public static void main(String[] args) {
        RandomDoubles rd = new RandomDoubles();
        for(int i=0; i<7 ;i++){
            System.out.println(rd.next() + " ");
        }
    }
}


下面我们使用适配器设计模式写一个类来进行适配。适配器模式有三种种, 这里是用的是类适配器模式, 还可以使用对象适配器模式


package net.mindview.interfaces;
import java.io.IOException;
import java.nio.CharBuffer;
import java.util.Scanner;
public class AdapterRandomDoubles extends RandomDoubles implements Readable{
    private int count;
    public AdapterRandomDoubles(int count){
        this.count = count;
    }
    @Override
    public int read(CharBuffer cb) throws IOException {
        if(count-- == 0){
            return -1;
        }
        String result = Double.toString(next()) + " ";
        cb.append(result);
        return result.length();
    }
    public static void main(String[] args) {
        Scanner s = new Scanner(new AdapterRandomDoubles(7));
        while(s.hasNextDouble()){
            System.out.println(s.nextDouble()+" ");
        }
    }
}


下面使用的是对象适配器模式, 和类适配器模式基本相似。


package net.mindview.interfaces;
import java.io.IOException;
import java.nio.CharBuffer;
import java.util.Scanner;
public class AdapterRandomDoubles implements Readable{
    private RandomDoubles rd;
    private int count;
    public AdapterRandomDoubles(RandomDoubles rd, int count){
        this.rd = rd;
        this.count = count;
    }
    @Override
    public int read(CharBuffer cb) throws IOException {
        if(count-- == 0){
            return -1;
        }
        String result = Double.toString(rd.next()) + " ";
        cb.append(result);
        return result.length();
    }
    public static void main(String[] args) {
        Scanner s = new Scanner(new AdapterRandomDoubles(new RandomDoubles(), 7));
        while(s.hasNextDouble()){
            System.out.println(s.nextDouble()+" ");
        }
    }
}


总结: 使用适配器模式, 我们可以在任何现有类之上添加类的接口,所以这意味着让方法接收接口类型,是一种让任何类都可以对该方法进行适配的方式。这就是使用接口而不使用类的强大之处。


 4.本章设计的最后一个设计模式: 工厂设计模式 参考文章:

 https://wenku.baidu.com/view/8293cb68302b3169a45177232f60ddccda38e62c.html###


     在面向对象编程中, 最通常的方法是一个new操作符产生一个对象实例,new操作符就是用来构造对象实例的。但是在一些情况下, new操作符直接生成对象会带来一些问题。举例来说, 许多类型对象的创造需要一系列的步骤: 你可能需要计算或取得对象的初始设置; 选择生成哪个子对象实例; 或在生成你需要的对象之前必须先生成一些辅助功能的对象。 在这些情况,新对象的建立就是一个 “过程”,不仅是一个操作,像一部大机器中的一个齿轮传动。


  模式的问题:你如何能轻松方便地构造对象实例,而不必关心构造对象实例的细节和复杂过程呢?


  解决方案:建立一个工厂来创建对象


实现:


一、引言


   1)还没有工厂时代:假如还没有工业革命,如果一个客户要一款宝马车,一般的做法是客户去创建一款宝马车,然后拿来用。


   2)简单工厂模式:后来出现工业革命。用户不用去创建宝马车。因为客户有一个工厂来帮他创建宝马.想要什么车,这个工厂就可以建。比如想要320i系列车。工厂就创建这个系列的车。即工厂可以创建产品。


   3)工厂方法模式时代:为了满足客户,宝马车系列越来越多,如320i,523i,30li等系列一个工厂无法创建所有的宝马系列。于是由单独分出来多个具体的工厂。每个具体工厂创建一种系列。即具体工厂类只能创建一个具体产品。但是宝马工厂还是个抽象。你需要指定某个具体的工厂才能生产车出来。


  4)抽象工厂模式时代:随着客户的要求越来越高,宝马车必须配置空调。于是这个工厂开始生产宝马车和需要的空调。


最终是客户只要对宝马的销售员说:我要523i空调车,销售员就直接给他523i空调车了。而不用自己去创建523i空调车宝马车.


这就是工厂模式。


二、分类


       工厂模式主要是为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的。


工厂模式可以分为三类:

1)简单工厂模式(Simple Factory)

2)工厂方法模式(Factory Method)

3)抽象工厂模式(Abstract Factory)

这三种模式从上到下逐步抽象,并且更具一般性。
       GOF在《设计模式》一书中将工厂模式分为两类:工厂方法模式(Factory Method)与抽象工厂模式(Abstract Factory)。

       将简单工厂模式(Simple Factory)看为工厂方法模式的一种特例,两者归为一类。

三、区别


工厂方法模式:

  • 一个抽象产品类,可以派生出多个具体产品类。
  • 一个抽象工厂类,可以派生出多个具体工厂类。
  • 每个具体工厂类只能创建一个具体产品类的实例。

抽象工厂模式:

 

  • 多个抽象产品类,每个抽象产品类可以派生出多个具体产品类。  
  • 一个抽象工厂类,可以派生出多个具体工厂类。  
  • 每个具体工厂类可以创建多个具体产品类的实例。  

  区别:
    工厂方法模式只有一个抽象产品类,而抽象工厂模式有多个。  
    工厂方法模式的具体工厂类只能创建一个具体产品类的实例,而抽象工厂模式可以创建多个。
    两者皆可。

 

四、简单工厂模式


建立一个工厂(一个函数或一个类方法)来制造新的对象。分布说明引子:从无到有。客户自己创建宝马车,然后拿来用。微信图片_20220507173400.png

相关文章
|
11天前
|
设计模式 安全 Java
Java编程中的单例模式:理解与实践
【10月更文挑战第31天】在Java的世界里,单例模式是一种优雅的解决方案,它确保一个类只有一个实例,并提供一个全局访问点。本文将深入探讨单例模式的实现方式、使用场景及其优缺点,同时提供代码示例以加深理解。无论你是Java新手还是有经验的开发者,掌握单例模式都将是你技能库中的宝贵财富。
16 2
|
6天前
|
JSON Java Apache
非常实用的Http应用框架,杜绝Java Http 接口对接繁琐编程
UniHttp 是一个声明式的 HTTP 接口对接框架,帮助开发者快速对接第三方 HTTP 接口。通过 @HttpApi 注解定义接口,使用 @GetHttpInterface 和 @PostHttpInterface 等注解配置请求方法和参数。支持自定义代理逻辑、全局请求参数、错误处理和连接池配置,提高代码的内聚性和可读性。
|
13天前
|
Java API Apache
Java编程如何读取Word文档里的Excel表格,并在保存文本内容时保留表格的样式?
【10月更文挑战第29天】Java编程如何读取Word文档里的Excel表格,并在保存文本内容时保留表格的样式?
67 5
|
8天前
|
安全 Java 编译器
JDK 10中的局部变量类型推断:Java编程的简化与革新
JDK 10引入的局部变量类型推断通过`var`关键字简化了代码编写,提高了可读性。编译器根据初始化表达式自动推断变量类型,减少了冗长的类型声明。虽然带来了诸多优点,但也有一些限制,如只能用于局部变量声明,并需立即初始化。这一特性使Java更接近动态类型语言,增强了灵活性和易用性。
91 53
|
7天前
|
存储 安全 Java
Java多线程编程的艺术:从基础到实践####
本文深入探讨了Java多线程编程的核心概念、应用场景及其实现方式,旨在帮助开发者理解并掌握多线程编程的基本技能。文章首先概述了多线程的重要性和常见挑战,随后详细介绍了Java中创建和管理线程的两种主要方式:继承Thread类与实现Runnable接口。通过实例代码,本文展示了如何正确启动、运行及同步线程,以及如何处理线程间的通信与协作问题。最后,文章总结了多线程编程的最佳实践,为读者在实际项目中应用多线程技术提供了宝贵的参考。 ####
|
4天前
|
监控 安全 Java
Java中的多线程编程:从入门到实践####
本文将深入浅出地探讨Java多线程编程的核心概念、应用场景及实践技巧。不同于传统的摘要形式,本文将以一个简短的代码示例作为开篇,直接展示多线程的魅力,随后再详细解析其背后的原理与实现方式,旨在帮助读者快速理解并掌握Java多线程编程的基本技能。 ```java // 简单的多线程示例:创建两个线程,分别打印不同的消息 public class SimpleMultithreading { public static void main(String[] args) { Thread thread1 = new Thread(() -> System.out.prin
|
6天前
|
存储 缓存 安全
在 Java 编程中,创建临时文件用于存储临时数据或进行临时操作非常常见
在 Java 编程中,创建临时文件用于存储临时数据或进行临时操作非常常见。本文介绍了使用 `File.createTempFile` 方法和自定义创建临时文件的两种方式,详细探讨了它们的使用场景和注意事项,包括数据缓存、文件上传下载和日志记录等。强调了清理临时文件、确保文件名唯一性和合理设置文件权限的重要性。
18 2
|
7天前
|
Java UED
Java中的多线程编程基础与实践
【10月更文挑战第35天】在Java的世界中,多线程是提升应用性能和响应性的利器。本文将深入浅出地介绍如何在Java中创建和管理线程,以及如何利用同步机制确保数据一致性。我们将从简单的“Hello, World!”线程示例出发,逐步探索线程池的高效使用,并讨论常见的多线程问题。无论你是Java新手还是希望深化理解,这篇文章都将为你打开多线程的大门。
|
8天前
|
安全 Java 编译器
Java多线程编程的陷阱与最佳实践####
【10月更文挑战第29天】 本文深入探讨了Java多线程编程中的常见陷阱,如竞态条件、死锁、内存一致性错误等,并通过实例分析揭示了这些陷阱的成因。同时,文章也分享了一系列最佳实践,包括使用volatile关键字、原子类、线程安全集合以及并发框架(如java.util.concurrent包下的工具类),帮助开发者有效避免多线程编程中的问题,提升应用的稳定性和性能。 ####
34 1
|
11天前
|
存储 设计模式 分布式计算
Java中的多线程编程:并发与并行的深度解析####
在当今软件开发领域,多线程编程已成为提升应用性能、响应速度及资源利用率的关键手段之一。本文将深入探讨Java平台上的多线程机制,从基础概念到高级应用,全面解析并发与并行编程的核心理念、实现方式及其在实际项目中的应用策略。不同于常规摘要的简洁概述,本文旨在通过详尽的技术剖析,为读者构建一个系统化的多线程知识框架,辅以生动实例,让抽象概念具体化,复杂问题简单化。 ####