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

相关文章
|
4天前
|
Java 程序员 开发者
深入理解Java并发编程:线程同步与锁机制
【4月更文挑战第30天】 在多线程的世界中,确保数据的一致性和线程间的有效通信是至关重要的。本文将深入探讨Java并发编程中的核心概念——线程同步与锁机制。我们将从基本的synchronized关键字开始,逐步过渡到更复杂的ReentrantLock类,并探讨它们如何帮助我们在多线程环境中保持数据完整性和避免常见的并发问题。文章还将通过示例代码,展示这些同步工具在实际开发中的应用,帮助读者构建对Java并发编程深层次的理解。
|
4天前
|
Java
Java并发编程:深入理解线程池
【4月更文挑战第30天】本文将深入探讨Java并发编程中的一个重要主题——线程池。我们将从线程池的基本概念入手,了解其工作原理和优势,然后详细介绍如何使用Java的Executor框架创建和管理线程池。最后,我们将讨论一些高级主题,如自定义线程工厂和拒绝策略。通过本文的学习,你将能够更好地理解和使用Java的线程池,提高你的并发编程能力。
|
4天前
|
安全 Java 调度
深入理解Java并发编程:线程安全与性能优化
【4月更文挑战第30天】本文将深入探讨Java并发编程的核心概念,包括线程安全、同步机制、锁优化以及性能调优。我们将通过实例分析如何确保多线程环境下的数据一致性,同时介绍一些常见的并发模式和最佳实践,旨在帮助开发者在保证线程安全的同时,提升系统的性能和响应能力。
|
1天前
|
Java 调度 开发者
Java 并发编程的探索与实践
【5月更文挑战第3天】在当今多核处理器普及的时代,并发编程已经成为提高程序性能的重要手段。本文将深入探讨 Java 并发编程的基本概念、原理及其在实际项目中的应用,帮助读者更好地理解和掌握 Java 并发编程技巧。
|
2天前
|
XML 监控 安全
18:面向切面编程-Java Spring
18:面向切面编程-Java Spring
19 5
|
2天前
|
并行计算 安全 Java
Java 并发编程的探索之旅
【5月更文挑战第2天】 在多线程的世界里,程序的行为变得错综复杂。本文将带您走进 Java 并发编程的核心概念,通过深入分析并发工具的使用和原理,帮助您构建高效、安全且响应迅速的应用程序。我们将探讨线程的基本知识,同步机制,以及高级并发工具如 Executors、Futures 和 Streams。通过理论与实践相结合的方式,为开发者提供一份清晰、实用的并发编程指南。
9 2
|
3天前
|
存储 安全 Java
深入理解Java并发编程:线程安全与性能优化
【5月更文挑战第1天】本文将深入探讨Java并发编程的核心概念,包括线程安全和性能优化。我们将详细分析线程安全问题的根源,以及如何通过合理的设计和编码实践来避免常见的并发问题。同时,我们还将探讨如何在保证线程安全的前提下,提高程序的并发性能,包括使用高效的同步机制、减少锁的竞争以及利用现代硬件的并行能力等技术手段。
|
4天前
|
安全 Java 程序员
Java并发编程:理解并应用ReentrantLock
【4月更文挑战第30天】 在多线程的世界中,高效且安全地管理共享资源是至关重要的。本文深入探讨了Java中的一种强大同步工具——ReentrantLock。我们将从其设计原理出发,通过实例演示其在解决并发问题中的实际应用,以及如何比传统的synchronized关键字提供更灵活的锁定机制。文章还将讨论在使用ReentrantLock时可能遇到的一些挑战和最佳实践,帮助开发者避免常见陷阱,提高程序性能和稳定性。
|
4天前
|
缓存 Java 调度
Java并发编程:深入理解线程池
【4月更文挑战第30天】 在Java并发编程中,线程池是一种重要的工具,它可以帮助我们有效地管理线程,提高系统性能。本文将深入探讨Java线程池的工作原理,如何使用它,以及如何根据实际需求选择合适的线程池策略。
|
4天前
|
Java
Java并发编程:深入理解线程池
【4月更文挑战第30天】 本文将深入探讨Java中的线程池,解析其原理、使用场景以及如何合理地利用线程池提高程序性能。我们将从线程池的基本概念出发,介绍其内部工作机制,然后通过实例演示如何创建和使用线程池。最后,我们将讨论线程池的优缺点以及在实际应用中需要注意的问题。