文章目录
1. 设计模式有哪些种类
- 创建型模式:提供创建对象的机制,提升已有代码的灵活性和可复用性
- 结构性模式:介绍如何将对象和类组装成较大的结构,并保持结构的灵活和高效
- 行为模式:介绍对象间的高效沟通和职责传递委派。
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. 基本介绍
原则是尽量使用合成/聚合的方式,而不是使用继承