Java23种设计模式系列——设计原则day1-2

181 篇文章 3 订阅
14 篇文章 0 订阅

软件设计原则

在软件开发中,为了提高软件系统的可维护性和可复用性,增加软件的可扩展性和灵活性,程序员要尽量根据6条原则来开发程序,从而提高软件开发效率、节约软件开发成本和维护成本。

开闭原则

对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级。
想要达到这样的效果,我们需要使用接口和抽象类。
因为抽象灵活性好,适应性广,只要抽象的合理,可以基本保持软件架构的稳定。而软件中易变的细节可以从抽象派生来的实现类来进行扩展,当软件需要发生变化时,只需要根据需求重新派生一个实现类来扩展就可以了。

开闭原则示例

架构即思路

整体来说使用Skill作为主接口,由AbstractUserSkill细化以及构造公用的方法,实际在UserSkill中进行实现
在这里插入图片描述

在这里插入图片描述

代码
Skill接口

专注于提供抽象的方法即可

package open_and_close;

public interface Skill {

     void showSkills();
     void setUserSkills();
}

AbstractUserSkill

实现Skill接口,写公用方法showSkills

package open_and_close;

import java.util.ArrayList;

public abstract class AbstractUserSkill implements Skill{
    public static final int SPECIAL_ID = 10000;
    protected ArrayList<String> userSkills;

    @Override
    public void showSkills() {
        this.userSkills.forEach(System.out::println);
    }
    @Override
    public abstract void setUserSkills();
}

UserSkill1
package open_and_close;

import java.util.ArrayList;

public class UserSkill1 extends AbstractUserSkill {
    public static final int SPECIAL_ID = 10001;
    private ArrayList<String> userSkills = new ArrayList<>();

    public UserSkill1() {
        this.setUserSkills();
    }

    @Override
    public void setUserSkills() {
        this.userSkills.add("UserSkill1:skill1");
        this.userSkills.add("UserSkill1:skill2");
        super.userSkills = this.userSkills;
    }
}

UserSkill2
package open_and_close;

import java.util.ArrayList;

public class UserSkill2 extends AbstractUserSkill {
    public static final int SPECIAL_ID = 10002;
    private ArrayList<String> userSkills = new ArrayList<>();

    public UserSkill2() {
        this.setUserSkills();
    }

    @Override
    public void setUserSkills() {
        this.userSkills.add("UserSkill2:skill1");
        this.userSkills.add("UserSkill2:skill2");
        super.userSkills = this.userSkills;
    }

}

测试
测试入口类

Game类是对Skill的应用实现

package open_and_close.apps;


import open_and_close.Skill;

public class Game1 {
    private Skill skill;

    public Game1(Skill skill) {
        this.skill = skill;
    }

    public Game1() {
    }

    public void setSkill(Skill skill) {
        this.skill = skill;
    }

    public void changeUserSkill() {
        this.skill.showSkills();
    }
}


测试类

真正的业务代码

package open_and_close;

import open_and_close.apps.Game1;

import java.util.Scanner;

public class Test {
    public static void main(String[] args) {
        Game1 game = new Game1();
        System.out.println("选择角色(输入:1,2)");
        Scanner scanner = new Scanner(System.in);
        int chooseID = scanner.nextInt();
        if (chooseID == UserSkill1.SPECIAL_ID - AbstractUserSkill.SPECIAL_ID) {
            game.setSkill(new UserSkill1());
        }
        if (chooseID == UserSkill2.SPECIAL_ID - AbstractUserSkill.SPECIAL_ID) {
            game.setSkill(new UserSkill2());
        }

        game.changeUserSkill();
    }
}

结果

在这里插入图片描述

里氏代换原则

里氏代换原则是面向对象设计的基本原则之一。
里氏代换原则:任何基类可以出现的地方,子类一定可以出现。
通俗理解︰子类可以扩展父类的功能,但不能改变父类原有的功能。换句话说,子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。
如果通过重写父类的方法来完成新的功能,这样写起来虽然简单,但是整个继承体系的可复用性会比较差,特别是运用多态比较频繁时,程序运行出错的概率会非常大。

依赖倒转原则

高层模块不应该依赖低层模块,两者都应该依赖其抽象,抽象不应该依赖细节,细节应该依赖抽象。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。

即在一个类(高层)中,有一个属性是类(低层)的抽象类或接口,而不是一个实习类

依赖倒转原则示例

设计一个手机的充电器接口

架构即思路

在这里插入图片描述

代码
Charger接口
package depend_reverse;

public interface Charger {
    void runCharger();
}

MiCharger实现类
package depend_reverse;

public class MiCharger implements Charger{
    private String name;
    private int power;
    private double price;

    public MiCharger() {
        this.name = "小米充电器";
        this.power = 220;
        this.price = 39.9;
    }

    @Override
    public void runCharger() {
        System.out.println(name);
        System.out.println(power);
        System.out.println(price);
    }
}

测试类(Phone)
package depend_reverse;

public class Phone {
    private Charger charger;

    public Charger getCharger() {
        return charger;
    }

    public void setCharger(Charger charger) {
        this.charger = charger;
    }

    public void getElectric(){
        charger.runCharger();
    }

    public static void main(String[] args) {
        Phone phone = new Phone();
        phone.setCharger(new MiCharger());
        phone.getElectric();
    }
}

结果

在这里插入图片描述

接口隔离原则

客户端不应该被迫依赖于它不使用的方法,一个类对另一个类的依赖应该建立在最小的接口上。
即我们应该将各类抽取为一个个接口,使用到才实现,不使用实现

接口隔离原则示例

架构即思路

两个接口各防止需要实现的方法,老年机只要实现Call,SendMsg不需要也就不用实现接口
在这里插入图片描述

代码
SendMsg
package interface_insulate;

public interface SendMsg {
    void sendMsg();
}

Call
package interface_insulate;

public interface Call {
    void Call();
}

OldPhone
package interface_insulate;

public class OldPhone implements Call{
    private String name;
    private String function;

    @Override
    public void Call() {
        this.function = "calling ...";
        this.name = "老年机";
        System.out.println(name+":"+function);
    }
}

测试
package interface_insulate;

public class Test {
    public static void main(String[] args) {
        OldPhone oldPhone = new OldPhone();
        oldPhone.Call();
    }
}

结果

在这里插入图片描述

迪米特法则

又叫最少知识原则
只和你的直接朋友交谈,不跟"陌生人"说话
其含义是:如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性。
迪米特法则中的"朋友"是指:当前对象本身、当前对象的成员对象、当前对象所创建的对象、当前对象的方法参数等,这些对象同当前对象存在关联、聚合或组合关系,可以直接访问这些对象的方法

迪米特示例

架构即思路

商品买卖时只有中间商知道卖家和买家,买家和卖家互不认识
在这里插入图片描述

代码
Seller
package demite;

public class Seller {
    private String name;
    private int money;
    private String goods;

    public Seller(String name) {
        this.name = name;
        this.goods = "二手书";
        this.money = 0;
    }

    public String getName() {
        return name;
    }

    public String getGoods() {
        return goods;
    }

    public void sell(String goods, int price, Agent agent){
        agent.showGoods(goods,price);
  }

    public void getMoney(String goods,int price) {
        this.goods = "";
        this.money += price;
        System.out.println("卖了:"+goods+"赚了:"+price+"还剩下:"+this.goods);
    }
}

Buyer
package demite;

public class Buyer {
    private String name;
    private int money;
    private String goods;

    public Buyer(String name) {
        this.name = name;
        this.money = 500;
        this.goods = null;
    }

    public void buy(String goods,int cost,Agent agent){
        agent.send(goods,cost);

    }

    public void costMoney(String goods,int cost){
        this.goods = goods;
        this.money -= cost;
        System.out.println("买了:"+goods+"花了:"+cost+"还剩下:"+this.money);
    }



}

Agent
package demite;

import java.util.HashMap;
import java.util.Objects;

public class Agent {
    private Buyer buyer;
    private Seller seller;
    private HashMap<String, Object> temp = new HashMap<>();

    public Agent(Buyer buyer, Seller seller) {
        this.buyer = buyer;
        this.seller = seller;
    }

    public Agent() {
    }

    public Buyer getBuyer() {
        return buyer;
    }

    public void setBuyer(Buyer buyer) {
        this.buyer = buyer;
    }

    public Seller getSeller() {
        return seller;
    }

    public void setSeller(Seller seller) {
        this.seller = seller;
    }

    public void send(String goods, int cost) {
        this.temp.remove("goods", goods);
        this.temp.remove("price", cost);

        if (this.temp.size() == 0) {
            this.buyer.costMoney(goods, cost);
            this.seller.getMoney(goods, cost);
        }

        System.out.println("交易完成");
    }

    public void showGoods(String goods, int price) {
        System.out.println(this.seller.getName() + "卖:" + goods + "价格为:" + price);
        this.temp.put("goods", goods);
        this.temp.put("price", price);
    }
}

测试
package demite;

public class Test {
    public static void main(String[] args) {
        Agent agent = new Agent();
        Seller seller = new Seller("张三");
        Buyer buyer = new Buyer("小王");
        agent.setBuyer(buyer);
        agent.setSeller(seller);

        seller.sell(seller.getGoods(),30,agent);

        buyer.buy(seller.getGoods(),30,agent);
    }
}

结果

在这里插入图片描述

合成复用原则

合成复用原则是指:尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。
通常类的复用分为继承复用和合成复用两种。
继承复用虽然有简单和易实现的优点,但它也存在以下缺点:

  1. 继承复用破坏了类的封装性。因为继承会将父类的实现细节暴露给子类,父类对子类是透明的,所以这种复用又称为白箱"复用。
  2. 子类与父类的耦合度高。父类的实现的任何改变都会导致子类的实现发生变化,这不利于类的扩展与维护。
  3. 它限制了复用的灵活性。从父类继承而来的实现是静态的,在编译时已经定义,所以在运行时不可能发生变化。

采用组合或聚合复用时,可以将已有对象纳入新对象中,使之成为新对象的一部分
新对象可以调用已有对象的功能,它有以下优点:

  1. 它维持了类的封装性。因为成分对象的内部细节是新对象看不见的,所以这种复用又称为"黑箱"复用。
  2. 对象间的耦合度低。可以在类的成员位置声明抽象。
  3. 复用的灵活性高。这种复用可以在运行时动态进行,新对象可以动态地引用与成分对象类型相同的对象。

总结

  1. 开闭原则:扩展易维护,接口为规范,实现为使用,实现热插拔
  2. 里氏代换:不要质疑前人的智慧,我们要站在巨人的肩膀上(子类不重写,只扩展功能)
  3. 依赖倒转:对抽象进行编程,使用抽象,隐藏具体实现,降低耦合
  4. 接口隔离:用多少,拿多少,对接口方法实现必要的,不出现任何冗余
  5. 迪米特:中介助力降低风险提升效率
  6. 合成复用:高内聚低耦合(多聚合,少继承),易扩展易复用
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值