设计模式七大原则介绍

1. 设计模式有哪些种类

  1. 创建型模式:提供创建对象的机制,提升已有代码的灵活性和可复用性
  2. 结构性模式:介绍如何将对象和类组装成较大的结构,并保持结构的灵活和高效
  3. 行为模式:介绍对象间的高效沟通和职责传递委派。

2. 设计模式的目的

  编写软件过程中,程序员面临着来自 耦合性,内聚性以及可维护性,可扩展性,重用性,灵活性 等多方面的挑战,设计模式是为了让程序(软件),具有更好的

  • 代码重用性 (即:相同功能的代码,不用多次编写)
  • 可读性 (即:编程规范性, 便于其他程序员的阅读和理解)
  • 可扩展性 (即:当需要增加新的功能时,非常的方便,称为可维护)
  • 可靠性 (即:当我们增加新的功能后,对原来的功能没有影响)
  • 使程序呈现高内聚,低耦合的特性

3. 设计模式七大原则

设计原则核心思想:

  • 找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。
  • 针对接口编程,而不是针对实现编程。
  • 为了交互对象之间的松耦合设计而努力

3.1. 单一职责原则

1. 基本介绍

  对类来说的,即一个类应该只负责一项职责。如类A负责两个不同职责:职责1,职责2。当职责1需求变更而改变A时,可能造成职责2执行错误,所以需要将类A的粒度分解为A1,A2

2. 模拟场景

这里通过视频网站例子来说明,当在各视频网站看电影、电视剧,网站针对不同用户类型,网站可以给出不通的反馈。

违背原则方案

public class VideoUserService {

//面对频繁迭代的业务需求,这样的代码结构就很难支撑,每一次的实现都可能影响其他逻辑,给整个结果口服务带来不可控的风险
    public void serveGrade(String userType){
        if ("VIP用户".equals(userType)){
            System.out.println("VIP用户,视频1080P蓝光");
        } else if ("普通用户".equals(userType)){
            System.out.println("普通用户,视频720P超清");
        } else if ("访客用户".equals(userType)){
            System.out.println("访客用户,视频480P高清");
        }
    }
}


@Test
public void test_serveGrade(){
     VideoUserService service = new VideoUserService();
     service.serveGrade("VIP用户");
     service.serveGrade("普通用户");
     service.serveGrade("访客用户");
 }

改善代码

//定义接口
public interface IVideoUserService {

    // 视频清晰级别;480P、720P、1080P
    void definition();

    // 广告播放方式;无广告、有广告
    void advertisement();

}

//访客用户
public class GuestVideoUserService implements IVideoUserService {

    public void definition() {
        System.out.println("访客用户,视频480P高清");
    }

    public void advertisement() {
        System.out.println("访客用户,视频有广告");
    }

}
//普通用户
public class OrdinaryVideoUserService implements IVideoUserService {

    public void definition() {
        System.out.println("普通用户,视频720P超清");
    }

    public void advertisement() {
        System.out.println("普通用户,视频有广告");
    }

}
//VIP用户
public class VipVideoUserService implements IVideoUserService {

    public void definition() {
        System.out.println("VIP用户,视频1080P蓝光");
    }

    public void advertisement() {
        System.out.println("VIP用户,视频无广告");
    }

}
//测试
public class ApiTest {

    @Test
    public void test_VideoUserService(){

        IVideoUserService guest = new GuestVideoUserService();
        guest.definition();
        guest.advertisement();

        IVideoUserService ordinary = new OrdinaryVideoUserService();
        ordinary.definition();
        ordinary.advertisement();

        IVideoUserService vip = new VipVideoUserService();
        vip.definition();
        vip.advertisement();

    }
}

2. 接口隔离原则

1. 基本介绍

   客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上

2. 模拟场景

违背原则实例

//总接口
interface Interface1 {
	void operation1();
	void operation2();
	void operation3();
	void operation4();
	void operation5();
}

class B implements Interface1 {
	public void operation1() {
		System.out.println("B 实现了 operation1");
	}
	
	public void operation2() {
		System.out.println("B 实现了 operation2");
	}
	public void operation3() {
		System.out.println("B 实现了 operation3");
	}
	public void operation4() {
		System.out.println("B 实现了 operation4");
	}
	public void operation5() {
		System.out.println("B 实现了 operation5");
	}
}

class D implements Interface1 {
	public void operation1() {
		System.out.println("D 实现了 operation1");
	}
	
	public void operation2() {
		System.out.println("D 实现了 operation2");
	}
	public void operation3() {
		System.out.println("D 实现了 operation3");
	}
	public void operation4() {
		System.out.println("D 实现了 operation4");
	}
	public void operation5() {
		System.out.println("D 实现了 operation5");
	}
}

//A 类通过接口Interface1 依赖(使用) B类,但是只会用到1,2,3方法
class A { 
	public void depend1(Interface1 i) {
		i.operation1();
	}
	public void depend2(Interface1 i) {
		i.operation2();
	}
	public void depend3(Interface1 i) {
		i.operation3();
	}
}

//C 类通过接口Interface1 依赖(使用) D类,但是只会用到1,4,5方法
class C { 
	public void depend1(Interface1 i) {
		i.operation1();
	}
	public void depend4(Interface1 i) {
		i.operation4();
	}
	public void depend5(Interface1 i) {
		i.operation5();
	}
}

改善代码UML图
在这里插入图片描述

3. 依赖倒转原则

1. 基本介绍

依赖倒转原则(Dependence Inversion Principle)是指:

  • 高层模块不应该依赖低层模块,二者都应该依赖其抽象
  • 抽象不应该依赖细节,细节应该依赖抽象
  • 依赖倒转(倒置)的中心思想是面向接口编程
  • 依赖倒转原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定的多
  • 使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成

2. 模拟场景

比如某互联网举办抽奖活动,抽奖活动的方式会随机变化的,比如随机抽奖、权重抽奖等。

违反原则方案

//投注用户
public class BetUser {

    private String userName;  // 用户姓名
    private int userWeight;   // 用户权重

    public BetUser() {
    }

    public BetUser(String userName, int userWeight) {
        this.userName = userName;
        this.userWeight = userWeight;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public int getUserWeight() {
        return userWeight;
    }

    public void setUserWeight(int userWeight) {
        this.userWeight = userWeight;
    }
}
//抽奖方式
public class DrawControl {

    // 随机
    public List<BetUser> doDrawRandom(List<BetUser> list, int count) {
        // 集合数量很小直接返回
        if (list.size() <= count) return list;
        // 乱序集合
        Collections.shuffle(list);
        // 取出指定数量的中奖用户
        List<BetUser> prizeList = new ArrayList<>(count);
        for (int i = 0; i < count; i++) {
            prizeList.add(list.get(i));
        }
        return prizeList;
    }

    // 权重
    public List<BetUser> doDrawWeight(List<BetUser> list, int count) {
        // 按照权重排序
        list.sort((o1, o2) -> {
            int e = o2.getUserWeight() - o1.getUserWeight();
            if (0 == e) return 0;
            return e > 0 ? 1 : -1;
        });
        // 取出指定数量的中奖用户
        List<BetUser> prizeList = new ArrayList<>(count);
        for (int i = 0; i < count; i++) {
            prizeList.add(list.get(i));
        }
        return prizeList;
    }

}

//测试
 @Test
public void test_DrawControl(){
     List<BetUser> betUserList = new ArrayList<>();
     betUserList.add(new BetUser("花花", 65));
     betUserList.add(new BetUser("豆豆", 43));
     betUserList.add(new BetUser("小白", 72));
     betUserList.add(new BetUser("笨笨", 89));
     betUserList.add(new BetUser("丑蛋", 10));

     DrawControl drawControl = new DrawControl();
     List<BetUser> prizeRandomUserList = drawControl.doDrawRandom(betUserList, 3);
     logger.info("随机抽奖,中奖用户名单:{}", JSON.toJSON(prizeRandomUserList));

     List<BetUser> prizeWeightUserList = drawControl.doDrawWeight(betUserList, 3);
     logger.info("权重抽奖,中奖用户名单:{}", JSON.toJSON(prizeWeightUserList));
 }

这样的实现方式扩展起来很麻烦,最后代码行数越多,不好维护

改善代码

//抽奖接口
public interface IDraw {

    // 获取中奖用户接口
    List<BetUser> prize(List<BetUser> list, int count);

}

//随机抽奖实现
public class DrawRandom implements IDraw {

    @Override
    public List<BetUser> prize(List<BetUser> list, int count) {
        // 集合数量很小直接返回
        if (list.size() <= count) return list;
        // 乱序集合
        Collections.shuffle(list);
        // 取出指定数量的中奖用户
        List<BetUser> prizeList = new ArrayList<>(count);
        for (int i = 0; i < count; i++) {
            prizeList.add(list.get(i));
        }
        return prizeList;
    }

}

//权重抽奖
public class DrawWeightRank implements IDraw {

    @Override
    public List<BetUser> prize(List<BetUser> list, int count) {
        // 按照权重排序
        list.sort((o1, o2) -> {
            int e = o2.getUserWeight() - o1.getUserWeight();
            if (0 == e) return 0;
            return e > 0 ? 1 : -1;
        });
        // 取出指定数量的中奖用户
        List<BetUser> prizeList = new ArrayList<>(count);
        for (int i = 0; i < count; i++) {
            prizeList.add(list.get(i));
        }
        return prizeList;
    }

}

//调用控制器
public class DrawControl {

    private IDraw draw;

    public List<BetUser> doDraw(IDraw draw, List<BetUser> betUserList, int count) {
        return draw.prize(betUserList, count);
    }

}

//
@Test
public void test_DrawControl() {

     List<BetUser> betUserList = new ArrayList<>();
     betUserList.add(new BetUser("花花", 65));
     betUserList.add(new BetUser("豆豆", 43));
     betUserList.add(new BetUser("小白", 72));
     betUserList.add(new BetUser("笨笨", 89));
     betUserList.add(new BetUser("丑蛋", 10));

     DrawControl drawControl = new DrawControl();
     List<BetUser> prizeRandomUserList = drawControl.doDraw(new DrawRandom(), betUserList, 3);
     logger.info("随机抽奖,中奖用户名单:{}", JSON.toJSON(prizeRandomUserList));

     List<BetUser> prizeWeightUserList = drawControl.doDraw(new DrawWeightRank(), betUserList, 3);
     logger.info("权重抽奖,中奖用户名单:{}", JSON.toJSON(prizeWeightUserList));
 }

4. 里氏替换原则

1. 基本介绍

  • 在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类的方法。
  • 里氏替换原则告诉我们,继承实际上让两个类耦合性增强了,在适当的情况下,可以通过聚合,组合,依赖 来解决问题。.

2. 模拟场景

假设在构建银行系统是,储蓄卡是第一类,信用卡是第二类,为了让信用卡可以使用储蓄卡额一些方法,选择信用卡继承储蓄卡。

违背原则方案

//储蓄卡
public class CashCard {

    private Logger logger = LoggerFactory.getLogger(CashCard.class);

    /**
     * 提现
     *
     * @param orderId 单号
     * @param amount  金额
     * @return 状态码 0000成功、0001失败、0002重复
     */
    public String withdrawal(String orderId, BigDecimal amount) {
        // 模拟支付成功
        logger.info("提现成功,单号:{} 金额:{}", orderId, amount);
        return "0000";
    }

    /**
     * 储蓄
     *
     * @param orderId 单号
     * @param amount  金额
     */
    public String recharge(String orderId, BigDecimal amount) {
        // 模拟充值成功
        logger.info("储蓄成功,单号:{} 金额:{}", orderId, amount);
        return "0000";
    }

    /**
     * 交易流水查询
     * @return 交易流水
     */
    public List<String> tradeFlow() {
        logger.info("交易流水查询成功");
        List<String> tradeList = new ArrayList<String>();
        tradeList.add("100001,100.00");
        tradeList.add("100001,80.00");
        tradeList.add("100001,76.50");
        tradeList.add("100001,126.00");
        return tradeList;
    }

}
//信用卡

public class CreditCard extends CashCard {

    private Logger logger = LoggerFactory.getLogger(CashCard.class);

    @Override
    public String withdrawal(String orderId, BigDecimal amount) {
        // 校验
        if (amount.compareTo(new BigDecimal(1000)) >= 0){
            logger.info("贷款金额校验(限额1000元),单号:{} 金额:{}", orderId, amount);
            return "0001";
        }
        // 模拟生成贷款单
        logger.info("生成贷款单,单号:{} 金额:{}", orderId, amount);
        // 模拟支付成功
        logger.info("贷款成功,单号:{} 金额:{}", orderId, amount);
        return "0000";
    }

    @Override
    public String recharge(String orderId, BigDecimal amount) {
        // 模拟生成还款单
        logger.info("生成还款单,单号:{} 金额:{}", orderId, amount);
        // 模拟还款成功
        logger.info("还款成功,单号:{} 金额:{}", orderId, amount);
        return "0000";
    }

    @Override
    public List<String> tradeFlow() {
        return super.tradeFlow();
    }

}

//测试
 @Test
   public void test_CashCard() {
       CashCard cashCard = new CashCard();
       // 提现
       cashCard.withdrawal("100001", new BigDecimal(100));
       // 储蓄
       cashCard.recharge("100001", new BigDecimal(100));
       // 交易流水
       List<String> tradeFlow = cashCard.tradeFlow();
       logger.info("查询交易流水,{}", JSON.toJSONString(tradeFlow));
   }

   @Test
   public void test_CreditCard() {
       CreditCard creditCard = new CreditCard();
       // 支付
       creditCard.withdrawal("100001", new BigDecimal(100));
       // 还款
       creditCard.recharge("100001", new BigDecimal(100));
       // 交易流水
       List<String> tradeFlow = creditCard.tradeFlow();
       logger.info("查询交易流水,{}", JSON.toJSONString(tradeFlow));
   }

这种集成复用了父类核心功能,但也破环了原有的方法。

改善代码

储蓄卡和信用卡在功能使用上有很多类似,可以将共同的属性及逻辑提取出一个抽象类,由抽象类定义所有卡的共同核心属性、逻辑。

//抽象银行卡类
public abstract class BankCard {

    private Logger logger = LoggerFactory.getLogger(BankCard.class);

    private String cardNo;   // 卡号
    private String cardDate; // 开卡时间

    public BankCard(String cardNo, String cardDate) {
        this.cardNo = cardNo;
        this.cardDate = cardDate;
    }

    abstract boolean rule(BigDecimal amount);

    // 正向入账,+ 钱
    public String positive(String orderId, BigDecimal amount) {
        // 入款成功,存款、还款
        logger.info("卡号{} 入款成功,单号:{} 金额:{}", cardNo, orderId, amount);
        return "0000";
    }

    // 逆向入账,- 钱
    public String negative(String orderId, BigDecimal amount) {
        // 入款成功,存款、还款
        logger.info("卡号{} 出款成功,单号:{} 金额:{}", cardNo, orderId, amount);
        return "0000";
    }

    /**
     * 交易流水查询
     *
     * @return 交易流水
     */
    public List<String> tradeFlow() {
        logger.info("交易流水查询成功");
        List<String> tradeList = new ArrayList<String>();
        tradeList.add("100001,100.00");
        tradeList.add("100001,80.00");
        tradeList.add("100001,76.50");
        tradeList.add("100001,126.00");
        return tradeList;
    }

    public String getCardNo() {
        return cardNo;
    }

    public String getCardDate() {
        return cardDate;
    }
}

//模拟储蓄卡功能
public class CashCard extends BankCard {

    private Logger logger = LoggerFactory.getLogger(CashCard.class);

    public CashCard(String cardNo, String cardDate) {
        super(cardNo, cardDate);
    }

    boolean rule(BigDecimal amount) {
        return true;
    }

    /**
     * 提现
     *
     * @param orderId 单号
     * @param amount  金额
     * @return 状态码 0000成功、0001失败、0002重复
     */
    public String withdrawal(String orderId, BigDecimal amount) {
        // 模拟支付成功
        logger.info("提现成功,单号:{} 金额:{}", orderId, amount);
        return super.negative(orderId, amount);
    }

    /**
     * 储蓄
     *
     * @param orderId 单号
     * @param amount  金额
     */
    public String recharge(String orderId, BigDecimal amount) {
        // 模拟充值成功
        logger.info("储蓄成功,单号:{} 金额:{}", orderId, amount);
        return super.positive(orderId, amount);
    }

    /**
     * 风险校验
     *
     * @param cardNo  卡号
     * @param orderId 单号
     * @param amount  金额
     * @return 状态
     */
    public boolean checkRisk(String cardNo, String orderId, BigDecimal amount) {
        // 模拟风控校验
        logger.info("风控校验,卡号:{} 单号:{} 金额:{}", cardNo, orderId, amount);
        return true;
    }

}

//信用卡
public class CreditCard extends CashCard {

    private Logger logger = LoggerFactory.getLogger(CreditCard.class);

    public CreditCard(String cardNo, String cardDate) {
        super(cardNo, cardDate);
    }

    boolean rule2(BigDecimal amount) {
        return amount.compareTo(new BigDecimal(1000)) <= 0;
    }

    /**
     * 提现,信用卡贷款
     *
     * @param orderId 单号
     * @param amount  金额
     * @return 状态码
     */
    public String loan(String orderId, BigDecimal amount) {
        boolean rule = rule2(amount);
        if (!rule) {
            logger.info("生成贷款单失败,金额超限。单号:{} 金额:{}", orderId, amount);
            return "0001";
        }
        // 模拟生成贷款单
        logger.info("生成贷款单,单号:{} 金额:{}", orderId, amount);
        // 模拟支付成功
        logger.info("贷款成功,单号:{} 金额:{}", orderId, amount);
        return super.negative(orderId, amount);

    }

    /**
     * 还款,信用卡还款
     *
     * @param orderId 单号
     * @param amount  金额
     * @return 状态码
     */
    public String repayment(String orderId, BigDecimal amount) {
        // 模拟生成还款单
        logger.info("生成还款单,单号:{} 金额:{}", orderId, amount);
        // 模拟还款成功
        logger.info("还款成功,单号:{} 金额:{}", orderId, amount);
        return super.positive(orderId, amount);
    }

}
//test

@Test
public void test_bankCard() {
      logger.info("里氏替换前,CashCard类:");
      CashCard bankCard = new CashCard("6214567800989876", "2020-10-01");
      // 提现
      bankCard.withdrawal("100001", new BigDecimal(100));
      // 储蓄
      bankCard.recharge("100001", new BigDecimal(100));

      logger.info("里氏替换后,CreditCard类:");
      CashCard creditCard = new CreditCard("6214567800989876", "2020-10-01");
      // 提现
      creditCard.withdrawal("100001", new BigDecimal(1000000));
      // 储蓄
      creditCard.recharge("100001", new BigDecimal(100));
  }

  @Test
  public void test_CreditCard(){
      CreditCard creditCard = new CreditCard("6214567800989876", "2020-10-01");
      // 支付,贷款
      creditCard.loan("100001", new BigDecimal(100));
      // 还款
      creditCard.repayment("100001", new BigDecimal(100));
  }

5. 开闭原则

1. 基本介绍

  • 一个软件实体如类,模块和函数应该对扩展开放(对提供方),对修改关闭(对使用方)。用抽象构建框架,用实现扩展细节。
  • 当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。
  • 编程中遵循其它原则,以及使用设计模式的目的就是遵循开闭原则。

2. 模拟场景

对于外部的调用方来说,只要能体现出面向抽象编程,定义出接口并实现其方法,即不修改原有方法体,只通过继承方式进行扩展,都可以体现出开闭原则。

这里计算三种形状的面积,其中⚪型面积Π是3.14,但后续由于Π值取的精度对于某些场景不足,需要扩展

//接口
public interface ICalculationArea {

    /**
     * 计算面积,长方形
     *
     * @param x 长
     * @param y 宽
     * @return 面积
     */
    double rectangle(double x, double y);

    /**
     * 计算面积,三角形
     * @param x 边长x
     * @param y 边长y
     * @param z 边长z
     * @return  面积
     *
     * 海伦公式:S=√[p(p-a)(p-b)(p-c)] 其中:p=(a+b+c)/2
     */
    double triangle(double x, double y, double z);

    /**
     * 计算面积,圆形
     * @param r 半径
     * @return 面积
     *
     * 圆面积公式:S=πr²
     */
    double circular(double r);

}

// Π =3.14 
public class CalculationArea implements ICalculationArea {

    private final static double π = 3.14D;

    public double rectangle(double x, double y) {
        return x * y;
    }

    public double triangle(double x, double y, double z) {
        double p = (x + y + z) / 2;
        return Math.sqrt(p * (p - x) * (p - y) * (p - z));
    }

    public double circular(double r) {
        return π * r * r;
    }

}

//违背原则,如果不考虑开闭原则直接修改实现类的Π值,但这样做会破环稳定性,可以追加一个实现类,只修改Π的值
public class CalculationAreaExt extends CalculationArea {

    private final static double π = 3.141592653D;

    @Override
    public double circular(double r) {
        return π * r * r;
    }

}

6. 迪米特原则

1. 基本介绍

  • 一个对象应该对其他对象保持最少的了解
  • 类与类关系越密切,耦合度越大
  • 迪米特法则(Demeter Principle)又叫最少知道原则,即一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外除了提供的public 方法,不对外泄露任何信息

2. 模拟场景

本例通过学生、老师、校长关系来说明

违背原则

//学生
public class Student {

    private String name;    // 学生姓名
    private int rank;       // 考试排名(总排名)
    private double grade;   // 考试分数(总分)

    public Student() {
    }

    public Student(String name, int rank, double grade) {
        this.name = name;
        this.rank = rank;
        this.grade = grade;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getRank() {
        return rank;
    }

    public void setRank(int rank) {
        this.rank = rank;
    }

    public double getGrade() {
        return grade;
    }

    public void setGrade(double grade) {
        this.grade = grade;
    }
}
//老师
public class Teacher {

    private String name;                // 老师名称
    private String clazz;               // 班级
    private static List<Student> studentList;  // 学生

    public Teacher() {
    }

    public Teacher(String name, String clazz) {
        this.name = name;
        this.clazz = clazz;
    }

    static {
        studentList = new ArrayList<>();
        studentList.add(new Student("花花", 10, 589));
        studentList.add(new Student("豆豆", 54, 356));
        studentList.add(new Student("秋雅", 23, 439));
        studentList.add(new Student("皮皮", 2, 665));
        studentList.add(new Student("蛋蛋", 19, 502));
    }

    public static List<Student> getStudentList() {
        return studentList;
    }

    public String getName() {
        return name;
    }

    public String getClazz() {
        return clazz;
    }
}
//校长
public class Principal {

    private Teacher teacher = new Teacher("丽华", "3年1班");

    // 查询班级信息,总分数、学生人数、平均值
    public Map<String, Object> queryClazzInfo(String clazzId) {
        // 获取班级信息;学生总人数、总分、平均分
        int stuCount = clazzStudentCount();
        double totalScore = clazzTotalScore();
        double averageScore = clazzAverageScore();

        // 组装对象,实际业务开发会有对应的类
        Map<String, Object> mapObj = new HashMap<>();
        mapObj.put("班级", teacher.getClazz());
        mapObj.put("老师", teacher.getName());
        mapObj.put("学生人数", stuCount);
        mapObj.put("班级总分数", totalScore);
        mapObj.put("班级平均分", averageScore);
        return mapObj;
    }

    // 总分
    public double clazzTotalScore() {
        double totalScore = 0;
        for (Student stu : teacher.getStudentList()) {
            totalScore += stu.getGrade();
        }
        return totalScore;
    }

    // 平均分
    public double clazzAverageScore(){
        double totalScore = 0;
        for (Student stu : teacher.getStudentList()) {
            totalScore += stu.getGrade();
        }
        return totalScore / teacher.getStudentList().size();
    }

    // 班级人数
    public int clazzStudentCount(){
        return teacher.getStudentList().size();
    }

}

//test 因为校长需要了解每个同学的信息,如果所有的班级都要校长来统计,代码会很臃肿,也不易扩展
public class ApiTest {

    private Logger logger = LoggerFactory.getLogger(ApiTest.class);

    @Test
    public void test_Principal() {
        Principal principal = new Principal();
        Map<String, Object> map = principal.queryClazzInfo("3年1班");
        logger.info("查询结果:{}", JSON.toJSONString(map));
    }

}

改善代码

public class Teacher {

    private String name;                // 老师名称
    private String clazz;               // 班级
    private static List<Student> studentList;  // 学生

    public Teacher() {
    }

    public Teacher(String name, String clazz) {
        this.name = name;
        this.clazz = clazz;
    }

    static {
        studentList = new ArrayList<>();
        studentList.add(new Student("花花", 10, 589));
        studentList.add(new Student("豆豆", 54, 356));
        studentList.add(new Student("秋雅", 23, 439));
        studentList.add(new Student("皮皮", 2, 665));
        studentList.add(new Student("蛋蛋", 19, 502));
    }

    // 总分
    public double clazzTotalScore() {
        double totalScore = 0;
        for (Student stu : studentList) {
            totalScore += stu.getGrade();
        }
        return totalScore;
    }

    // 平均分
    public double clazzAverageScore(){
        double totalScore = 0;
        for (Student stu : studentList) {
            totalScore += stu.getGrade();
        }
        return totalScore / studentList.size();
    }

    // 班级人数
    public int clazzStudentCount(){
        return studentList.size();
    }

    public String getName() {
        return name;
    }

    public String getClazz() {
        return clazz;
    }
}

//老师
public class Principal {

    private Teacher teacher = new Teacher("丽华", "3年1班");

    // 查询班级信息,总分数、学生人数、平均值
    public Map<String, Object> queryClazzInfo(String clazzId) {
        // 获取班级信息;学生总人数、总分、平均分
        int stuCount = teacher.clazzStudentCount();
        double totalScore = teacher.clazzTotalScore();
        double averageScore = teacher.clazzAverageScore();

        // 组装对象,实际业务开发会有对应的类
        Map<String, Object> mapObj = new HashMap<>();
        mapObj.put("班级", teacher.getClazz());
        mapObj.put("老师", teacher.getName());
        mapObj.put("学生人数", stuCount);
        mapObj.put("班级总分数", totalScore);
        mapObj.put("班级平均分", averageScore);
        return mapObj;
    }

}

//test
 @Test
public void test_Principal() {
      Principal principal = new Principal();
      Map<String, Object> map = principal.queryClazzInfo("3年1班");
      logger.info("查询结果:{}", JSON.toJSONString(map));
  }

7. 合成复用原则

1. 基本介绍

原则是尽量使用合成/聚合的方式,而不是使用继承
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值