期末复习之设计模式

103 阅读6分钟

设计模式简要总结

用于期末考试复习

策略模式

使用场景

当我们需要用户传入的不同参数来决定不同的方法策略时,可以用到此模式。 根据不同的情况来选择不同的策略

用云顶之奕举例子: 我们可以根据不同的海克斯选择不同的策略去选择阵容。

代码实现

策略接口

public interface Lineup {

    /**
     * 挑选阵容
     */
    void selectLineup();

}

阵容具体实现

public class DuelMaster implements Lineup{
    @Override
    public void selectLineup() {
        System.out.println("八决斗灭魂劫嗷嗷乱杀");
    }
}
public class DefendOfStar implements Lineup{
    @Override
    public void selectLineup() {
        System.out.println("无所谓,我星守岩雀会出手!");
    }
}
public class MoDaoTuan implements Lineup{
    @Override
    public void selectLineup() {
        System.out.println("嘿,这里是老八魔盗团!");
    }
}

玩家类

public class Player {

    private Lineup lineup;

    Player(Lineup lineup){
        this.lineup = lineup;
    }

    public void action(){
        lineup.selectLineup();
    }

}

main

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(new BufferedInputStream(System.in));
        Player player;
        System.out.println("输入你选择的海克斯");
        while(true){
            String hex = scanner.nextLine();
            switch (hex){
                case "地下魔盗团之魂" :
                    player = new Player(new MoDaoTuan());
                    break;
                case "决斗大师之魂" :
                    player = new Player(new DuelMaster());
                    break;
                case "珠光莲花" :
                    player = new Player(new DefendOfStar());
                    break;
                default:
                    throw new IllegalStateException("Unexpected value: " + hex);
            }
            player.action();
        }
    }
}

运行结果

image.png

容易困惑的问题

他与工厂模式的区别是什么

策略模式是根据行为设计的模式 他针对的是策略的封装,用户可以知道全部的策略。

工厂模式是根据创建对象设计的模式,他是针对于对象的创建,便于统一资源管理,相关对象的创建由工厂类处理,不必暴露给用户

uml图

image.png

访问者模式

使用场景

在一个相对稳定的数据结构中,封装一些对于数据结构中元素的操作,从而不改变这个数据结构。

依旧是云顶的例子哈,在每一场游戏中,一个人的棋盘的元素是相对稳定的,现在假设只有经济和等级(其他元素先省略) 我作为访问者和其他人作为访问者又是不一样的

代码实现

public interface Visitor {

    void visit(Level level);

    void visit(Money money);

}
public class Owner implements Visitor{
    
    @Override
    public void visit(Level level) {
        System.out.println("你现在等级是"+level.getNum()+"级,还差"+level.getNeed()+"经验升级,卷死老哥们!");
    }

    @Override
    public void visit(Money money) {
        System.out.println("你现在有"+money.getNum()+"块钱,备战席卖掉卡利息啊!");
    }
}
public class Other implements Visitor{
    @Override
    public void visit(Level level) {
        System.out.println("这货现在"+level.getNum()+"级了,卷闷了");
    }

    @Override
    public void visit(Money money) {
        System.out.println("他现在保底有"+money.getStar()+"0块钱,经济酱紫高?");
    }
}
public interface GameItem {
    void accept(Visitor visitor);
}
public class Level implements GameItem{

    /**
     * 等级
     */
    private int num;

    /**
     * 还差多少升下一等级
     */
    private int need;

    public Level(int num, int need) {
        this.num = num;
        this.need = need;
    }

    public int getNum() {
        return num;
    }

    public int getNeed() {
        return need;
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}
public class Money implements GameItem {

    /**
     * 每十块钱地图侧边就有一颗星
     */
    private int star;

    /**
     * 具体数值
     */
    private int num;

    public Money(int star, int num) {
        this.star = star;
        this.num = num;
    }

    public int getStar() {
        return star;
    }

    public int getNum() {
        return num;
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}
public class Chessboard {
    private List<GameItem> itemList = new ArrayList<>();
    Chessboard(){
        itemList.add(new Level(5,10));
        itemList.add(new Money(5,52));
    }
    public void show(Visitor visitor){
        for(GameItem g:itemList){
            g.accept(visitor);
        }
    }
}
public class Main {
    public static void main(String[] args) {
        //初始化一下棋盘
        Chessboard chessboard = new Chessboard();
        //我自己看棋盘
        System.out.println("我自己访问");
        System.out.println("----------------");
        chessboard.show(new Owner());
        System.out.println("----------------");
        //别人看棋盘
        System.out.println("别人访问");
        System.out.println("----------------");
        chessboard.show(new Other());
        System.out.println("----------------");
    }
}

运行结果

image.png

容易困惑的问题

并不知道visit方法和accept方法实际上是干什么用的只有模模糊糊的概念。

在visitor接口中,定义了好多visit方法。每一个方法都是针对某个具体元素的访问。

上面说过,这个数据结构是相对稳定的,也就是说,我们后续不需要对这个接口在做更改,便于编码

而accept方法则是表示这个元素接收的是哪个访问者的访问,根据不同访问者提供不同的作用

uml图

image.png

建造者模式

使用场景

当我们需要构建一个相对复杂的对象时,我们可以把他的构建与表示分离,从而优化设计。

当类的构造函数的参数很多时,并且不全是必要属性,我们就可以考虑使用建造者模式。

一辆汽车有轮子,邮箱和车身,我们可以构建奔驰汽车和宝马汽车,使用建造者模式实现

代码实现

定义一个汽车

public class CarProduct {
    private String wheel;
    private String oilBox;
    private String body;

    public void setWheel(String wheel) {
        this.wheel = wheel;
    }

    public void setOilBox(String oilBox) {
        this.oilBox = oilBox;
    }

    public void setBody(String body) {
        this.body = body;
    }
    public void show(){
        System.out.println("1."+wheel+" 2."+oilBox+" 3."+body);
    }
}

builder类

public abstract class Builder {
    protected CarProduct carProduct = new CarProduct();
    public abstract void buildWheel();
    public abstract void buildOilBox();
    public abstract void buildBody();
    public CarProduct getResult(){
        return carProduct;
    }
}

具体的builder

public class BMWBuilder extends Builder{
    @Override
    public void buildWheel() {
        carProduct.setWheel("BMW的轮胎");
    }

    @Override
    public void buildOilBox() {
        carProduct.setOilBox("BMW的油箱");
    }

    @Override
    public void buildBody() {
        carProduct.setBody("BMW的车身");
    }
}
public class BenZBuilder extends Builder{
    @Override
    public void buildWheel() {
        carProduct.setWheel("BenZ的轮胎");
    }

    @Override
    public void buildOilBox() {
        carProduct.setOilBox("BenZ的油箱");
    }

    @Override
    public void buildBody() {
        carProduct.setBody("BenZ的车身");
    }
}

director

public class Director {

    private Builder builder;

    Director(Builder builder){
        this.builder = builder;
    }

    public CarProduct construct(){
        builder.buildWheel();
        builder.buildOilBox();
        builder.buildBody();
        return builder.getResult();
    }

}

客户端

public class Client {
    public static void main(String[] args) {
        Builder bmwBuilder = new BMWBuilder();
        Builder benZBuilder = new BenZBuilder();
        Director director = new Director(bmwBuilder);
        CarProduct construct = director.construct();
        construct.show();
        director = new Director(benZBuilder);
        CarProduct construct1 = director.construct();
        construct1.show();
    }
}

另一种实现方式

public class Car {
    private final String wheel;
    private final String oilBox;
    private final String body;

    private Car(Builder builder){
        this.wheel = builder.wheel;
        this.oilBox = builder.oilBox;
        this.body = builder.body;
    }
    public static class Builder{
        private String wheel;
        private String oilBox;
        private String body;

        public Builder(){
        }

        public Builder setWheel(String wheel) {
            this.wheel = wheel;
            return this;
        }
        public Builder setOilBox(String oilBox) {
            this.oilBox = oilBox;
            return this;
        }
        public Builder setBody(String body) {
            this.body = body;
            return this;
        }
        public Car build(){
            return new Car(this);
        }
    }
    public void show(){
        System.out.println("1."+wheel+" 2."+oilBox+" 3."+body);
    }
}

调用方式我觉得还挺优雅

public class Client {
    public static void main(String[] args) {
        Car car = new Car.Builder()
                .setOilBox("BMW的油箱")
                .setBody("BenZ的车身")
                .setWheel("中国制造的轮胎")
                .build();
        car.show();
    }
}

第二种的实现方式经常可以在一些框架当中见到

uml图

image.png

适配器模式

使用场景

类似于一个转接头吧,现在很多手机都没有耳机孔了,需要一个适配器来转换typeC和耳机孔。

代码实现

public class EarChicken {
    public void listenMusic(){
        System.out.println("有线耳机已经连接");
    }
}
public interface HuaWeiPhone {
    public void listen();
}
public class Adepter implements HuaWeiPhone{
    private EarChicken earChicken;
    Adepter(EarChicken earChicken){
        this.earChicken = earChicken;
    }
    @Override
    public void listen() {
        earChicken.listenMusic();
    }
}

uml图

image.png 挺简单的不多赘述

观察者模式

使用场景

首先有两方,一个是观察者,一个是被观察者,当被观察者发生变化时,观察者要做出相应改变。

挺绕的其实,要分清观察者和被观察者。 例子使用学校实验课说过的,商品如果价格和名字发生改变,则通知会员。

代码实现

首先定义观察者接口

public interface Observer {
    //这个方法就是观察者如果观察到变化后所做出的改变
    void update(String name,Double price);

}

然后定义被观察者 因为一个被观察者可以由多个观察者观察,因此需要用list存储观察者

public interface Subject {

    void registerObserver(Observer observer);

    void removeObserver(Observer observer);

    void notifyObserver();

}

被观察者的具体实现

public class ProductSubject implements Subject{

    private List<Observer> observers;

    private String name;

    private Double price;

    ProductSubject(){
        observers = new ArrayList();
    }

    @Override
    public void registerObserver(Observer observer) {
        observers.add(observer);
    }

    @Override
    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }

    @Override
    public void notifyObserver() {
        for(Observer o:observers){
            o.update(name,price);
        }
    }

    public void setProduct(String name,Double price){
        this.name = name;
        this.price = price;
        //当元素改变时,通知观察者
        notifyObserver();
    }
}

观察者的具体实现

public class ConcreteObserver implements Observer{

    private String name;

    private Double price;

    private Subject subject;

    ConcreteObserver(Subject subject){
        this.subject = subject;
        subject.registerObserver(this);
    }
    @Override
    public void update(String name, Double price) {
        this.name = name;
        this.price = price;

        System.out.println("接收到信息,并给你处理\n" +
                "商品名称为"+name+"  价格为"+price);
    }
}

可能会迷惑的

一定要分清观察者和被观察者,被观察者的类一定要设计三个方法,一个是添加观察者,一个是删除,一个是数据更新后提醒观察者。 而观察者中,有一个方法是update,就是用于观察到对象改变后所要进行的操作。

在移动应用开发中,经常可以见到,当我们对RecycleView的元素 操作发生变化时,我们会使用到notifyObserver(),这里就是用到了观察者模式。

uml图

image.png

桥接模式

使用场景

就跟他名字一样,如果我们想要的一个类中,有两个以上的元素可以独立扩展就可以将他拆分出来,需要的时候,在使用桥接模式拼在一起。

例子就用实验课上的,咖啡分大中小杯,可以加糖、奶之类的元素,最后生成一个我们想要的咖啡

代码实现

咖啡接口

public interface Coffee {
    void add(Thing thing);
    void drink();
}

咖啡料的接口

public interface Thing {
    void add();
}

具体咖啡实现(只列一种)

public class SmallCoffee implements Coffee{
    private Thing thing;
    @Override
    public void add(Thing thing) {
        this.thing = thing;
    }

    @Override
    public void drink() {
        System.out.print("我是小杯咖啡");
        thing.add();
    }
}

具体咖啡料实现(只列一种)

public class Milk implements Thing{

    @Override
    public void add() {
        System.out.println("加了牛奶。");
    }
}

这边结合了简单工厂模式制作了一个客户端

public class Factory {
    public Thing getThing(int type){
        switch (type){
            case 1 : return new Milk();
            case 2 : return new Sugar();
            case 3 : return new Lemon();
            default: return null;
        }
    }
    public Coffee getCoffee(int type){
        switch (type){
            case 1 : return new JorumCoffee();
            case 2 : return new MediumCoffee();
            case 3 : return new SmallCoffee();
            default: return null;
        }
    }
}
public class Client {
    public static void main(String[] args) {
        Factory factory = new Factory();
        Scanner scanner = new Scanner(System.in);
        while(true){
            System.out.println("选择你想喝的咖啡吧!\n" +
                    "1、大杯\n" +
                    "2、中杯\n" +
                    "3、小杯");
            Integer integer = Integer.parseInt(scanner.nextLine());
            Coffee coffee = factory.getCoffee(integer);
            System.out.println("加点什么?\n" +
                    "1、牛奶\n" +
                    "2、糖\n" +
                    "3、柠檬");
            coffee.add(factory.getThing(Integer.parseInt(scanner.nextLine())));
            coffee.drink();
        }
    }
}

容易困惑的

为什么使用桥接模式,因为如果上面的例子我要单独设置一个类去表示的话,我需要写3*3=9个类去分别代表不同的咖啡。 但如果我把它拆分开,我就只写了6个类,工作量大大降低。

uml图

image.png

装饰模式

使用场景

叠buff知道吧,就是给一个基本类增加几个特殊的属性,从而完成不同的工作。

继续是实验课的例子,喜羊羊吃苹果。 通过吃不同的苹果获得不同buff

代码实现

羊,基本类

public class Sheep {

    private Integer blood;

    public Integer getBlood(){
        return blood;
    }

    public void show(){
        System.out.println("我是一只🐏");
    }

    public Sheep(Integer blood) {
        this.blood = blood;
    }

    public boolean hurt(){
        this.blood--;
        if (blood==0){
            return false;
        }
        return true;
    }
}

装饰羊,后面的buff羊是通过继承装饰羊得以实现,不直接继承基本羊!!

public class DecoratorSheep extends Sheep{
    protected Sheep sheep;
    public DecoratorSheep(Sheep sheep) {
        super(sheep.getBlood());
        this.sheep = sheep;

    }

    @Override
    public void show() {
        if (sheep!=null){
            sheep.show();
        }
    }
}

buff羊(只给一个)

public class RedSheep extends DecoratorSheep{
    public RedSheep(Sheep sheep) {
        super(sheep);
    }

    @Override
    public void show() {
        super.show();
        System.out.println("我还吃了红苹果,有保护罩");
    }
}

客户端

public class Client {
    public static void main(String[] args) {
        System.out.println("游戏开始\n" +
                "请选择操作\n" +
                "1.被狼咬\n" +
                "2.吃红苹果\n" +
                "3.吃绿苹果\n" +
                "4.吃黄苹果\n" +
                "5.不想玩啦,退出!");
        Scanner scanner = new Scanner(System.in);
        Sheep sheep = new Sheep(5);
        DecoratorSheep gameSheep = new DecoratorSheep(sheep);
        boolean flag = true;
        while (flag){
            int type = scanner.nextInt();
            switch (type){
                case 1: {
                    if(!gameSheep.hurt()){
                        System.out.println("你死了");
                        flag = false;
                    }
                    System.out.println("还剩"+gameSheep.getBlood()+"血!");
                    break;
                }
                case 2: {
                    gameSheep = new RedSheep(gameSheep);
                    gameSheep.show();
                    break;
                }
                case 3: {
                    gameSheep = new GreenSheep(gameSheep);
                    gameSheep.show();
                    break;
                }
                case 4: {
                    gameSheep = new YellowSheep(gameSheep);
                    gameSheep.show();
                    break;
                }
                default:{
                    System.out.println("游戏结束,期待相遇");
                    flag = false;
                }
            }
        }

    }
}

容易困惑的

为什么buff羊不直接继承基本羊,要另外设计一个装饰羊来继承。 在基础羊中,我们需要通过构造函数维护基本羊的一些属性,在装饰模式中,装饰羊的构造方法是我们特别设计的,后续的buff羊可以直接利用这个构造方法进行装饰。

uml图

image.png