对于JAVA设计模式的自我修养

对于JAVA设计模式的自我修养

一 、工厂模式

这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

1. 抽象工厂模式

抽象工厂模式(Abstract Factory),提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

抽象工厂模式中,此例为A和B两家工厂,都生产小汽车和船,需要哪个工厂的船或者车子,在AFactoryThing 或者BFactoryThing中生产所需要的东西即可

类图

image-20210527103006439.png

案例设计思路

  1. 创建一个工厂接口包含A和B工厂生产的方法,返回值为A中产品的父类,即满足无需指定它们具体的类。
  2. 创建A、B工厂并实现1中的接口 ,A中生产A产品,若A生产B则非法权限,B反之
  3. 创建A、B两工厂的自营店,分别继承AB工厂,实现AB工厂的规章制度,变量和方法
  4. 最后将写入自己工厂的产品类,继承自己的自营店。

代码案例

public interface AbstractCreateFactory {
    AFactoryThing createAFactoryThing(int id);
    BFactoryThing createBFactoryThing(int id);

}
public class AFactoryThing  extends ProduceAFactory{
    public int price = 0;
    public String name = "A";

    public void toProduce() {
        System.out.println("这是我们A公司的" + name + "\t价格为" + price);
    }
//从超市买东西,超市自己从商店买东西
}
public  class BFactoryThing extends  ProduceBFactory {
    public int price = 0;
    public String name = "B";

    public void toProduce() {
        System.out.println("这是我们B公司的" + name + "\t价格为" + price);
    }
    
}
public class CarA extends AFactoryThing {
    public CarA() {
        price=9;
        name="车子";
        toProduce();
    }
}
public class CarB extends BFactoryThing {
    public CarB() {
        name = "车子";
        price = 10;
        toProduce();
    }
}

public class ProduceAFactory implements AbstractCreateFactory {
    @Override
    public AFactoryThing createAFactoryThing(int id) {
        AFactoryThing aFactoryThing = null;
        switch (id) {
            case 0:
                aFactoryThing = new CarA();
                break;
            case 1:
                aFactoryThing = new ShipA();

                break;
            default:
                aFactoryThing = new CarA();
                break;
        }

        return aFactoryThing;
    }

    @Override
    public BFactoryThing createBFactoryThing(int id) {
        System.out.println("A工厂没有权限生产B产品");
        return null;

    }
}
public class ProduceBFactory implements AbstractCreateFactory {
    @Override
    public AFactoryThing createAFactoryThing(int id) {
        System.out.println("B工厂没有权限生产A产品");
       return  null;
    }

    @Override
    public BFactoryThing createBFactoryThing(int id) {
        BFactoryThing bFactoryThing=null;

        switch (id) {
            case 0:
                bFactoryThing=new CarB();
                break;
            case 1:
                bFactoryThing=new ShipB();
                break;
            default:
                bFactoryThing=new CarB();

                break;
        }
        return bFactoryThing;
    }
}
public class ShipA extends AFactoryThing {


    public ShipA() {
        price = 19;
        name = "船";
        toProduce();
    }

}
public class ShipB extends BFactoryThing {

    public ShipB() {
        price = 20;
        name = "船";
        toProduce();
    }
}
public class Client {
    public static void main(String[] args) {
        AFactoryThing aFactoryThing = new AFactoryThing();
        BFactoryThing bFactoryThing = new BFactoryThing();
        aFactoryThing.createAFactoryThing(0);
        aFactoryThing.createAFactoryThing(1);
        bFactoryThing.createBFactoryThing(0);
        ShipB bFactoryThing1 = (ShipB)bFactoryThing.createBFactoryThing(1);
    }
}


2. 简单工厂模式

类图

image-20210527103111402.png
  • 我们把被创建的对象称为“产品”,把创建产品的对象称为“工厂”。如果要创建的产品不多,只要一个工厂类就可以完成,这种模式叫“简单工厂模式”。

案例设计思路

简单工厂模式的创作思路就更简单了,工厂接口类,本例中用到了操作类工厂,返回自己需要的类型即可

代码案例

public interface AbstractCalculateFactory {
    OperationCal createOperationCal(String message);
}
public class AddCalculate extends OperationCal {
    public  AddCalculate() {
        message=add;
        result = x + y;
        showResult();
    }
}
public class DivCalculate extends OperationCal {
    public DivCalculate() {
        message = div;
        result = x / y;
        showResult();
    }
}
public class MulCalculate extends  OperationCal{
    public  MulCalculate() {
        message=mul;
        result = x * y;
      showResult();
    }
}
public class ReduceCalculate extends OperationCal {
    public ReduceCalculate() {
        message = red;
        result = x - y;
        showResult();
    }
}
public class OperationCal implements AbstractCalculateFactory {
    public double x = 16;
    public double y = 8;
    public double result = 0;
    public String message = "";
    public static String add="ADD";
    public static String red="RED";
    public static String mul="MUL";
    public static String div="DIV";
    @Override
    public OperationCal createOperationCal(String message) {

        OperationCal operationCal = null;
        switch (message) {
            case "add":
                operationCal = new AddCalculate();
                break;
            case "red":
                operationCal = new ReduceCalculate();
                break;
            case "mul":
                operationCal = new MulCalculate();
                break;
            case "div":
                operationCal = new DivCalculate();
                break;
            default:
                operationCal = new DivCalculate();
                System.out.println("操作有误");
        }

        return operationCal;
    }

    public void showResult() {
        System.out.println(x + message + y + "的结果为" + result + "\n");
    }
}

public class OperationClient {
    public static void main(String[] args) {
      OperationCal operationCal=new OperationCal();
      operationCal.createOperationCal("div");
      operationCal.createOperationCal("mul");
      operationCal.createOperationCal("add");
      operationCal.createOperationCal("red");

    }
}

二、建造者模式

建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

类图

image-20210527103217571.png

抽象类实现接口后 不用实现接口的所有方法 ,非抽象类就得实现接口的所有方法

普通类继承抽象类后,得重写实现抽象类未实现的方法!

案例设计思路

  1. 本实例中创建了Item(商品类目)和Packing(打包) 的接口,Item描述了name,packing和price方法,Packing描述了pack方法

  2. 实现接口 注意 Item(商品类目)包含Burger(汉堡)和ColdDrink(冷饮)两个抽象类 (注意为何写抽象类,抽象类中的price()方法可以忽略,那是一个抽象方法,不是实现Item中的price方法)所以抽象类只实现了packing方法,剩下两个方法在子类中实现的.(层层搭建)

  3. 瓶装和袋装两个类实现Packing接口,实体类封装搭建完毕之后开始管理类的搭建

  4. 做菜单管理类 meal ,添加商品(添加商品直接为item对象,不管是点什么菜品都加入其中即可),计算价钱,返回商品

  5. 最后MealBuilder 菜单建造者类 相当于客人点菜类,点好菜后,Meal 返回。

代码案例

package com.example.xm.build;

import java.util.ArrayList;
import java.util.List;

public interface Item {
    public String name();
    public Packing packing();
    public float price();
}
interface Packing {
    public String pack();
}
 class Wrapper implements Packing {

    @Override
    public String pack() {
        return "Wrapper";
    }
}
 class Bottle implements Packing {

    @Override
    public String pack() {
        return "Bottle";
    }
}
abstract class Burger implements Item {

    @Override
    public Packing packing() {
        return new Wrapper();
    }


    @Override
    public abstract float price();
}
abstract class ColdDrink implements Item {

    @Override
    public Packing packing() {
        return new Bottle();
    }

    @Override
    public abstract float price();
}
class VegBurger extends Burger {
    @Override
    public String name() {
        return "Veg Burger";
    }

    @Override
    public float price() {
        return 0;
    }
}
class ChickenBurger extends Burger {

    @Override
    public float price() {
        return 50.5f;
    }

    @Override
    public String name() {
        return "Chicken Burger";
    }
}
class Coke extends ColdDrink {

    @Override
    public float price() {
        return 30.0f;
    }

    @Override
    public String name() {
        return "Coke";
    }
}
class Pepsi extends ColdDrink {

    @Override
    public float price() {
        return 35.0f;
    }

    @Override
    public String name() {
        return "Pepsi";
    }
}
class Meal {
    private List<Item> items = new ArrayList<Item>();

    public void addItem(Item item){
        items.add(item);
    }

    public float getCost(){
        float cost = 0.0f;
        for (Item item : items) {
            cost += item.price();
        }
        return cost;
    }

    public void showItems(){
        for (Item item : items) {
            System.out.print("Item : "+item.name());
            System.out.print(", Packing : "+item.packing().pack());
            System.out.println(", Price : "+item.price());
        }
    }
}
class MealBuilder {
    public Meal prepareVegMeal (){
        Meal meal = new Meal();
        meal.addItem(new VegBurger());
        meal.addItem(new Coke());
        return meal;
    }

    public Meal prepareNonVegMeal (){
        Meal meal = new Meal();
        meal.addItem(new ChickenBurger());
        meal.addItem(new Pepsi());
        return meal;
    }
}
class BuilderPatternDemo {
    public static void main(String[] args) {
        MealBuilder mealBuilder = new MealBuilder();

        Meal vegMeal = mealBuilder.prepareVegMeal();
        System.out.println("Veg Meal");
        vegMeal.showItems();
        System.out.println("Total Cost: " +vegMeal.getCost());

        Meal nonVegMeal = mealBuilder.prepareNonVegMeal();
        System.out.println("\n\nNon-Veg Meal");
        nonVegMeal.showItems();
        System.out.println("Total Cost: " +nonVegMeal.getCost());
    }
}

三、原型模式

原型模式是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。(通过克隆返回对象)

这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。(缓存对象,克隆返回对象)

类图

image-20210527103238348.png

原型模式中设计思路 加载缓存返回缓存对象

案例设计思路

  1. 代码以加载图形和缓存为案例,创建三种图形类(相当于数据库的操作)
  2. 然后将图形加入到缓存中,ShapeCache 中的loadCache方法
  3. 其次再用getShape(id) 返回所需要的图形对象

原型模式的优点就是高性能,需要某个对象的时候能够快速的构建出来

另外 原型模式可逃避构造函数的约束。不需要new 的方式构建新对象

代码案例

public abstract class Shape implements Cloneable {
    private String id;
    protected  String type="默认";
    abstract void draw();
    public String getType() {
        return type;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getId() {
        return id;
    }
    @Override
    protected Object clone() {
        Object clone = null;
        try {
            clone = super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return clone;
    } 
}

public class Circle extends Shape {

    public Circle() {
        type = "圆";
    }
    @Override
    void draw() {
        System.out.println("画了一个" + type);
    }
}

public class Rectangle extends Shape {
    public Rectangle() {
        type = "Rectangle";
    }
    @Override
    void draw() {
        System.out.println("画了一个"+type);
    }
}

public class Square extends Shape {
    public Square() {
        type = "Square";
    }
    @Override
    void draw() {
        System.out.println("画了一个"+type);
    }
}

public class ShapeCache { //形状缓存区
    private static Hashtable<String, Shape> shapeMap = new Hashtable<String, Shape>();

    //读取形状
     public static Shape getShape(String shapeId) {
        Shape cache = shapeMap.get(shapeId);
        return (Shape) cache.clone();

    }
     public static void loadCache() {
        Circle circle = new Circle();
        circle.setId("1");

        Square square = new Square();
        square.setId("2");

        Rectangle rectangle = new Rectangle();
        rectangle.setId("3");
        shapeMap.put(circle.getId(), circle);
        shapeMap.put(square.getId(), square);
        shapeMap.put(rectangle.getId(), rectangle);
    }
}

public class PrototypeClient {
    public static void main(String[] args) {
        ShapeCache.loadCache(); //加载缓存
        Shape shape = ShapeCache.getShape("1");
      shape.draw();
        Shape shape1 = ShapeCache.getShape("2");
        shape1.draw();
        Shape shape2 = ShapeCache.getShape("3");
        shape2.draw();
    }
}

四、适配器模式

适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。

这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。

适配器是什么,先从宏观上生活上,它就是一个用来适配某个东西来达到某种效果的一个物体,起到一个转换作用或者是红线作用

例子:本例子遵循菜鸟教程例子:有一个音频播放器,但是只支持mp3格式的播放,然后做了一个适配器,将音频播放器和适配器连接,播放mp4和vlc的音频,所以再音频播放器里直接调用适配器即可

类图

image-20210527113237917.png

案例设计思路

  1. 适配器模式的代码对菜鸟做了略微的修改,修改后相对于原先更美观,更简洁
  2. 首先建立一个接口AdvanceMediaPlayer ,用mp4和vlc 两个类实现
  3. 然后建立一个适配器MediaAdapter,实现播放mp4和vlc为文件
  4. 最后将原始播放器拿过来继承适配器,这样原来的播放器就会支持本来的mp3和现有的mp4,vlc文件
  5. 最后Client 将需要的文件播放

代码案例


public interface AdvanceMediaPlayer {
    void playVlc(String fileName);
    void playMp4(String fileName);
}

public interface MediaPlayer {
    void play(String audioTYpe,String fileName);
}

public class Mp4Player implements AdvanceMediaPlayer {

    @Override
    public void playVlc(String fileName) {
        System.out.println("文件不匹配");
    }

    @Override
    public void playMp4(String fileName) {
        System.out.println("播放了Mp4" + fileName);

    }
}

public class VlcPlayer implements AdvanceMediaPlayer {
    @Override
    public void playVlc(String fileName) {
        System.out.println("播放了VLC"+fileName);
    }

    @Override
    public void playMp4(String fileName) {
        System.out.println("文件不匹配");
    }
}

public class MediaAdapter implements MediaPlayer {
    private AdvanceMediaPlayer advanceMediaPlayer;

    public void prepareAudio(String audioType) {

        if (audioType.equalsIgnoreCase("vlc")) {
            advanceMediaPlayer = new VlcPlayer();
        } else if (audioType.equalsIgnoreCase("mp4")) {
            advanceMediaPlayer = new Mp4Player();
        }

    }


    @Override
    public void play(String audioType, String fileName) {
        if (audioType.equalsIgnoreCase("vlc")) {
            advanceMediaPlayer.playVlc(fileName);
        } else if (audioType.equalsIgnoreCase("mp4")) {
            advanceMediaPlayer.playMp4(fileName);
        }
    }
}

public class AudioPlay extends MediaAdapter{

    public AudioPlay() {
    }

    @Override
    public void play(String audioTYpe, String fileName) {
        if (audioTYpe.equalsIgnoreCase("vlc") || audioTYpe.equalsIgnoreCase("mp4")) {
            super.prepareAudio(audioTYpe);
            super.play(audioTYpe, fileName);
        } else if (audioTYpe.equalsIgnoreCase("mp3")) {
            System.out.println("播放mp3");
        } else {
            System.out.println("不支持该文件");
        }
    }
}

public class AdapterClient {
    public static void main(String[] args) {
        AudioPlay audioPlayer = new AudioPlay();
        audioPlayer.play("vlc","java.vlc");
        audioPlayer.play("mp4","java.mp4");
        audioPlayer.play("mp3","java.mp3");
        audioPlayer.play("m","java.m");
    }
}

五、桥接模式

桥接(Bridge)是用于把抽象化与实现化解耦,使得二者可以独立变化。这种类型的设计模式属于结构型模式,它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦。

类图

image-20210529124751423.png

案例设计思路

  1. 桥接模式抽象类调用接口,实现代码独立通过抽象类来聚合接口,调用抽象类的子类,完成程序
  2. 定义好DrawApi 接口类,并用绿圆和红圆实现接口
  3. 再定义图形Shape类,聚合接口类,构造传参数,并抽象一个draw的方法
  4. 最后定义Circle类,继承Shape,实现draw方法,该方法里聚合接口类的方法,完成桥接模式。

代码案例

interface  DrawApi {
    void drawCircle();
}

public class RedCircle  implements  DrawApi{
    @Override
    public void drawCircle() {
        System.out.println("画了一个红色的圆");
    }
}

public class GreenCircle  implements  DrawApi{
    @Override
    public void drawCircle() {
        System.out.println("画了一个绿色的圆");

    }
}

public abstract class Shape {
    public DrawApi drawApi;

    public Shape(DrawApi drawApi) {
        this.drawApi = drawApi;
    }
    abstract void draw();
}

public class Circle extends Shape {
    public Circle(DrawApi drawApi) {
        super(drawApi);
    }

    @Override
    void draw() {
       drawApi.drawCircle();
    }
}

public class BridgeClient {
    public static void main(String[] args) {
        RedCircle redCircle = new RedCircle();
        GreenCircle greenCircle = new GreenCircle();
        Shape circle = new Circle(redCircle);
        Shape circle1 = new Circle(greenCircle);
        circle.draw();
        circle1.draw();
    }
}

六、单例模式

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。1.

懒汉式,线程不安全

public class LazySingle {
    private static LazySingle lazySingle;
//调用实例方法时候再判断是否存在实例对象,线程不安全,操作不规范可能创建多个对象
    public static LazySingle getInstance() {
        if (lazySingle == null) {
            lazySingle = new LazySingle();
        }
        return lazySingle;
    }
}

懒汉式,线程安全

public class LazySingleSafe {
    private static LazySingleSafe lazySingleSafe;

    public static synchronized LazySingleSafe getInstance() {
//调用实例方法时候再判断是否存在实例对象,只不过方法加了个线程锁,防止多个线程同时进入
        if (lazySingleSafe == null) {
            lazySingleSafe = new LazySingleSafe();
        }
        return lazySingleSafe;
    }
}

饿汉式

public class HungrySingle {
    private static HungrySingle hungrySingle = new HungrySingle();
//先加载创建好对象后,调用实例方法直接返回静态对象
    public static HungrySingle getInstance() {
        return hungrySingle;
    }

}

双检锁/双重校验锁

public class DoubleCheckSingle {
    private static DoubleCheckSingle doubleCheckSingle;
//内部类 利用类加载器机制,进行同步锁,指挥加载一次,不会同时进入线程锁里面
    public DoubleCheckSingle getInstances() {
        if (doubleCheckSingle == null) {
            synchronized (DoubleCheckSingle.class) {
                if (doubleCheckSingle == null) {
                    doubleCheckSingle = new DoubleCheckSingle();
                }
                
            }

        }
        return doubleCheckSingle;
    }

}

登记式/静态内部类

//内部类方式的单例模式
public class StaticSingle {
    private static class StaticSingleHolder {
        private static StaticSingle single = new StaticSingle();
    }
//这种方式能达到双检锁方式一样的功效,但实现更简单。对静态域使用延迟初始化,
// 应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况,
// 双检锁方式可在实例域需要延迟初始化时使用。
    public StaticSingle getInstance() {

        return StaticSingleHolder.single;
    }
}

枚举

public enum EchoSingle {
    INSTANCE;
}
//枚举机制的单例
class Client {
    public static void main(String[] args) {
        EchoSingle echoSingle = EchoSingle.INSTANCE;

    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容