Java 设计模式(上)

目录

一、单一职责原则

二、开闭原则

三、里氏替换原则

四、迪米特法则

五、接口隔离原则

六、依赖倒置原则

七、工厂方法

八、抽象工厂

九、建造者模式

十、原型模式

十一、单例模式

十二、适配器模式


一、单一职责原则

单一职责原则又称单一功能原则,面向对象五基本原则之一。

单一职责原则定义:一个对象应该只包含单一的职责,并且该职责被完整地封装在一个类中。

错误示范:

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高清");}}}
    @Testvoid singleton() {VideoUserService service = new VideoUserService();service.serveGrade("VIP用户");service.serveGrade("普通用户");service.serveGrade("访客用户");}

正确代码

定义一个接口,分别来一个他们的子实现类

public interface IVideoUserService {void definition();void advertisement();}
public class VipVideoUserService implements IVideoUserService {public void definition() {System.out.println("VIP用户,视频1080P蓝光");}public void advertisement() {System.out.println("VIP会员,视频无广告");}
}
public class OrdinaryVideoUserService implements IVideoUserService {public void definition() {System.out.println("普通用户,视频720P超清");}public void advertisement() {System.out.println("普通用户,视频有广告");}
}
public class GuestVideoUserService implements IVideoUserService {public void definition() {System.out.println("访客用户,视频480P高清");}public void advertisement() {System.out.println("访客用户,视频有广告");}}
    void singleton02() {IVideoUserService guest = new GuestVideoUserService();guest.advertisement();guest.definition();}

二、开闭原则

开闭原则是:规定软件中的对象(其中包括类,模块,函数等等)应该对于扩展是开放的,但是对于修改是封闭的。

举个例子,我么要求圆的面积,π的大概值为3.14,第一个人需要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);}

第一个人的需求

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;@Overridepublic double circular(double r) {return π * r * r;}}

测试

    @Testvoid openClose01() {ICalculationArea area = new CalculationAreaExt();double circular = area.circular(10);System.out.println(circular);}

三、里氏替换原则

里氏替换原则,继承必须确保超类所拥有的性质在子类中依然成立。可以理解为,一个同名不同地方的餐馆,他的营销模式或者内容要一致,可以替换的,而不是风格全变了。

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);}}
    @Testvoid replace01() {logger.info("里氏替换前,CashCard类:");CashCard bankCard = new CashCard("6214567800989876", "2022-03-05");// 提现bankCard.withdrawal("100001", new BigDecimal(100));// 储蓄bankCard.recharge("100001", new BigDecimal(100));logger.info("里氏替换后,CreditCard类:");CashCard creditCard = new CreditCard("6214567800989876", "2022-03-05");// 提现creditCard.withdrawal("100001", new BigDecimal(1000000));// 储蓄creditCard.recharge("100001", new BigDecimal(100));}@Testvoid replace02() {CreditCard creditCard = new CreditCard("6214567800989876", "2022-03-05");// 支付,贷款creditCard.loan("100001", new BigDecimal(100));// 还款creditCard.repayment("100001", new BigDecimal(100));}

两张卡,做了同样的事情,结果是一致的,替换的方式是正确的。

四、迪米特法则

迪米特法则,意义在于降低类之间的耦合。由于每个对象尽量减少对其他对象的了解,因此,很容易使得系统的功能模块功能独立,相互之间不存在(或者有很少)依赖关系。也就是模块与模块之间最少知道,是单个模块能自己独立出来(高内聚,低耦合)。

举个例子,校长、学生、老师,校长应该直接可以观察老师,老师观察学生,而不是校长直接观察学生,而是老师来提供信息给校长。

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;}
}
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 static List<Student> getStudentList() {return studentList;}public String getName() {return name;}public String getClazz() {return clazz;}
}
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;}
}
    @Testvoid dimite() {Principal principal = new Principal();Map<String, Object> map = principal.queryClazzInfo("3年1班");logger.info("查询结果:{}", JSON.toJSONString(map));}

五、接口隔离原则

要求 程序员尽量将臃肿庞大的接口拆分为更小的和更具体的接口,让接口中只包含客户感兴趣的方法。更小的接口,更具体的接口(高内聚,低耦合)。

public interface ISkillArchery {// 射箭void doArchery();}
public interface ISkillInvisible {// 隐袭void doInvisible();}
public interface ISkillSilent {// 沉默void doSilent();}
public interface ISkillVertigo {// 眩晕void doVertigo();}
// 后裔
public class HeroHouYi implements ISkillArchery, ISkillInvisible, ISkillSilent {@Overridepublic void doArchery() {System.out.println("后裔的灼日之矢");}@Overridepublic void doInvisible() {System.out.println("后裔的隐身技能");}@Overridepublic void doSilent() {System.out.println("后裔的沉默技能");}}
// 廉颇
public class HeroLianPo implements ISkillInvisible, ISkillSilent, ISkillVertigo {@Overridepublic void doInvisible() {System.out.println("廉颇的隐身技能");}@Overridepublic void doSilent() {System.out.println("廉颇的沉默技能");}@Overridepublic void doVertigo() {System.out.println("廉颇的眩晕技能");}}
    @Testpublic void InterfaceSegregation(){// 后裔HeroHouYi heroHouYi = new HeroHouYi();heroHouYi.doArchery();// 廉颇HeroLianPo heroLianPo = new HeroLianPo();heroLianPo.doInvisible();}

六、依赖倒置原则

高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象(依赖接口,降低耦合),简单来说就是要求对抽象进行编程,不要对实现进行编程。其核心思想是:要面向接口编程,不要面向实现编程。依赖倒置原则是实现开闭原则的重要途径之一,它降低了客户与实现模块之间的耦合。

举个例子:上级给下级颁布任务,不可能是上级走到下级面前告诉他要完成xx任务,如果有n个下级,那上级就要走n次,这显然是不可能的,这时,我上级定义了一个标准,下级只需要按照这个标准提交任务即可,这样形成了一个依赖倒置。

// 抽奖接口
public interface IDraw {List<BetUser> prize(List<BetUser> list, int count);}
// 用户
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 {private IDraw draw;public List<BetUser> doDraw(IDraw draw, List<BetUser> betUserList, int count) {return draw.prize(betUserList, count);}}
// 随机抽奖
public class DrawRandom implements IDraw {@Overridepublic 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 {@Overridepublic 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;}}
    @Testpublic void DependencyInversion() {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));}

七、工厂方法

工厂模式又称工厂方法模式,是一种创建型设计模式,其在父类中提供一个创建对象的方法, 允许子类决定实例化对象的类型。

这种设计模式也是 Java 开发中最常见的一种模式,它的主要意图是定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。

简单说就是为了提供代码结构的扩展性,屏蔽每一个功能类中的具体实现逻辑。让外部可以更加简单的只是知道调用即可,同时,这也是去掉众多ifelse的方式。当然这可能也有一些缺点,比如需要实现的类非常多,如何去维护,怎样减低开发成本。但这些问题都可以在后续的设计模式结合使用中,逐步降低。

public interface ICommodity {void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception;}
// 工厂
public class StoreFactory {/*** 奖品类型方式实例化* @param commodityType 奖品类型* @return              实例化对象*/public ICommodity getCommodityService(Integer commodityType) {if (null == commodityType) return null;if (1 == commodityType) return new CouponCommodityService();if (2 == commodityType) return new GoodsCommodityService();if (3 == commodityType) return new CardCommodityService();throw new RuntimeException("不存在的奖品服务类型");}/*** 奖品类信息方式实例化* @param clazz 奖品类* @return      实例化对象*/public ICommodity getCommodityService(Class<? extends ICommodity> clazz) throws IllegalAccessException, InstantiationException {if (null == clazz) return null;return clazz.newInstance();}}
public class CardCommodityService implements ICommodity {private Logger logger = LoggerFactory.getLogger(CardCommodityService.class);// 模拟注入private IQiYiCardService iQiYiCardService = new IQiYiCardService();public void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception {String mobile = queryUserMobile(uId);iQiYiCardService.grantToken(mobile, bizId);logger.info("请求参数[爱奇艺兑换卡] => uId:{} commodityId:{} bizId:{} extMap:{}", uId, commodityId, bizId, JSON.toJSON(extMap));logger.info("测试结果[爱奇艺兑换卡]:success");}private String queryUserMobile(String uId) {return "15200101232";}}
public class CouponCommodityService implements ICommodity {private Logger logger = LoggerFactory.getLogger(CouponCommodityService.class);private CouponService couponService = new CouponService();public void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception {CouponResult couponResult = couponService.sendCoupon(uId, commodityId, bizId);logger.info("请求参数[优惠券] => uId:{} commodityId:{} bizId:{} extMap:{}", uId, commodityId, bizId, JSON.toJSON(extMap));logger.info("测试结果[优惠券]:{}", JSON.toJSON(couponResult));if (!"0000".equals(couponResult.getCode())) throw new RuntimeException(couponResult.getInfo());}}
public class GoodsCommodityService implements ICommodity {private Logger logger = LoggerFactory.getLogger(GoodsCommodityService.class);private GoodsService goodsService = new GoodsService();public void sendCommodity(String uId, String commodityId, String bizId, Map<String, String> extMap) throws Exception {DeliverReq deliverReq = new DeliverReq();deliverReq.setUserName(queryUserName(uId));deliverReq.setUserPhone(queryUserPhoneNumber(uId));deliverReq.setSku(commodityId);deliverReq.setOrderId(bizId);deliverReq.setConsigneeUserName(extMap.get("consigneeUserName"));deliverReq.setConsigneeUserPhone(extMap.get("consigneeUserPhone"));deliverReq.setConsigneeUserAddress(extMap.get("consigneeUserAddress"));Boolean isSuccess = goodsService.deliverGoods(deliverReq);logger.info("请求参数[实物商品] => uId:{} commodityId:{} bizId:{} extMap:{}", uId, commodityId, bizId, JSON.toJSON(extMap));logger.info("测试结果[实物商品]:{}", isSuccess);if (!isSuccess) throw new RuntimeException("实物商品发放失败");}private String queryUserName(String uId) {return "花花";}private String queryUserPhoneNumber(String uId) {return "15200101232";}}
    @Testpublic void FactoryMethod01() throws Exception {StoreFactory storeFactory = new StoreFactory();// 1. 优惠券ICommodity commodityService_1 = storeFactory.getCommodityService(1);commodityService_1.sendCommodity("10001", "EGM1023938910232121323432", "791098764902132", null);// 2. 实物商品ICommodity commodityService_2 = storeFactory.getCommodityService(2);commodityService_2.sendCommodity("10001", "9820198721311", "1023000020112221113", new HashMap<String, String>() {{put("consigneeUserName", "谢飞机");put("consigneeUserPhone", "15200292123");put("consigneeUserAddress", "吉林省.长春市.双阳区.XX街道.檀溪苑小区.#18-2109");}});// 3. 第三方兑换卡(模拟爱奇艺)ICommodity commodityService_3 = storeFactory.getCommodityService(3);commodityService_3.sendCommodity("10001", "AQY1xjkUodl8LO975GdfrYUio", null, null);}

    @Testpublic void FactoryMethod02() throws Exception {StoreFactory storeFactory = new StoreFactory();// 1. 优惠券ICommodity commodityService = storeFactory.getCommodityService(CouponCommodityService.class);commodityService.sendCommodity("10001", "EGM1023938910232121323432", "791098764902132", null);}

八、抽象工厂

抽象工厂模式与工厂方法模式虽然主要意图都是为了解决,接口选择问题。但在实现上,抽象工厂是一个中心工厂,创建其他工厂的模式。

    @Testpublic void AbstractFactory() throws Exception {CacheService proxy_EGM = JDKProxyFactory.getProxy(CacheService.class, EGMCacheAdapter.class);proxy_EGM.set("user_name_01", "xxx");String val01 = proxy_EGM.get("user_name_01");logger.info("缓存服务 EGM 测试,proxy_EGM.get 测试结果:{}", val01);CacheService proxy_IIR = JDKProxyFactory.getProxy(CacheService.class, IIRCacheAdapter.class);proxy_IIR.set("user_name_01", "yyy");String val02 = proxy_IIR.get("user_name_01");logger.info("缓存服务 IIR 测试,proxy_IIR.get 测试结果:{}", val02);}
public class JDKProxyFactory {public static <T> T getProxy(Class<T> cacheClazz, Class<? extends ICacheAdapter> cacheAdapter) throws Exception {InvocationHandler handler = new JDKInvocationHandler(cacheAdapter.newInstance());ClassLoader classLoader = Thread.currentThread().getContextClassLoader();return (T) Proxy.newProxyInstance(classLoader, new Class[]{cacheClazz}, handler);}}
public class JDKInvocationHandler implements InvocationHandler {private ICacheAdapter cacheAdapter;public JDKInvocationHandler(ICacheAdapter cacheAdapter) {this.cacheAdapter = cacheAdapter;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {return ICacheAdapter.class.getMethod(method.getName(), ClassLoaderUtils.getClazzByArgs(args)).invoke(cacheAdapter, args);}}
public interface ICacheAdapter {String get(final String key);void set(String key, String value);void set(String key, String value, long timeout, TimeUnit timeUnit);void del(String key);}
public class IIRCacheAdapter implements ICacheAdapter {private IIR iir = new IIR();@Overridepublic String get(String key) {return iir.get(key);}@Overridepublic void set(String key, String value) {iir.set(key, value);}@Overridepublic void set(String key, String value, long timeout, TimeUnit timeUnit) {iir.setExpire(key, value, timeout, timeUnit);}@Overridepublic void del(String key) {iir.del(key);}}
public class EGMCacheAdapter implements ICacheAdapter {private EGM egm = new EGM();public String get(String key) {return egm.gain(key);}public void set(String key, String value) {egm.set(key, value);}public void set(String key, String value, long timeout, TimeUnit timeUnit) {egm.setEx(key, value, timeout, timeUnit);}public void del(String key) {egm.delete(key);}}

九、建造者模式

建造者模式所完成的内容就是通过将多个简单对象通过一步步的组装构建出一个复杂对象的过程。

而这样的根据相同的物料,不同的组装所产生出的具体的内容,就是建造者模式的最终意图,也就是;将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。

    @Testpublic void test_Builder(){Builder builder = new Builder();// 豪华欧式System.out.println(builder.levelOne(132.52D).getDetail());// 轻奢田园System.out.println(builder.levelTwo(98.25D).getDetail());// 现代简约System.out.println(builder.levelThree(85.43D).getDetail());}
public class Builder {public IMenu levelOne(Double area) {return new DecorationPackageMenu(area, "豪华欧式").appendCeiling(new LevelTwoCeiling())    // 吊顶,二级顶.appendCoat(new DuluxCoat())             // 涂料,多乐士.appendFloor(new ShengXiangFloor());     // 地板,圣象}public IMenu levelTwo(Double area){return new DecorationPackageMenu(area, "轻奢田园").appendCeiling(new LevelTwoCeiling())   // 吊顶,二级顶.appendCoat(new LiBangCoat())           // 涂料,立邦.appendTile(new MarcoPoloTile());       // 地砖,马可波罗}public IMenu levelThree(Double area){return new DecorationPackageMenu(area, "现代简约").appendCeiling(new LevelOneCeiling())   // 吊顶,一级顶.appendCoat(new LiBangCoat())           // 涂料,立邦.appendTile(new DongPengTile());        // 地砖,东鹏}}
public interface IMenu {/*** 吊顶*/IMenu appendCeiling(Matter matter);/*** 涂料*/IMenu appendCoat(Matter matter);/*** 地板*/IMenu appendFloor(Matter matter);/*** 地砖*/IMenu appendTile(Matter matter);/*** 明细*/String getDetail();}
public interface Matter {/*** 场景;地板、地砖、涂料、吊顶*/String scene();/*** 品牌*/String brand();/*** 型号*/String model();/*** 平米报价*/BigDecimal price();/*** 描述*/String desc();}
// 装修包
public class DecorationPackageMenu implements IMenu {private List<Matter> list = new ArrayList<Matter>();  // 装修清单private BigDecimal price = BigDecimal.ZERO;      // 装修价格private BigDecimal area;  // 面积private String grade;     // 装修等级;豪华欧式、轻奢田园、现代简约private DecorationPackageMenu() {}public DecorationPackageMenu(Double area, String grade) {this.area = new BigDecimal(area);this.grade = grade;}public IMenu appendCeiling(Matter matter) {list.add(matter);price = price.add(area.multiply(new BigDecimal("0.2")).multiply(matter.price()));return this;}public IMenu appendCoat(Matter matter) {list.add(matter);price = price.add(area.multiply(new BigDecimal("1.4")).multiply(matter.price()));return this;}public IMenu appendFloor(Matter matter) {list.add(matter);price = price.add(area.multiply(matter.price()));return this;}public IMenu appendTile(Matter matter) {list.add(matter);price = price.add(area.multiply(matter.price()));return this;}public String getDetail() {StringBuilder detail = new StringBuilder("\r\n-------------------------------------------------------\r\n" +"装修清单" + "\r\n" +"套餐等级:" + grade + "\r\n" +"套餐价格:" + price.setScale(2, BigDecimal.ROUND_HALF_UP) + " 元\r\n" +"房屋面积:" + area.doubleValue() + " 平米\r\n" +"材料清单:\r\n");for (Matter matter: list) {detail.append(matter.scene()).append(":").append(matter.brand()).append("、").append(matter.model()).append("、平米价格:").append(matter.price()).append(" 元。\n");}return detail.toString();}}

十、原型模式

原型模式主要解决的问题就是创建重复对象,而这部分对象内容本身比较复杂,生成过程可能从库或者RPC接口中获取数据的耗时较长,因此采用克隆的方式节省时间。

举个例子,一场考试在保证大家的公平性一样的题目下,开始出现试题混排更有做的好的答案选项也混排。这样大大的增加了抄的成本,也更好的做到了考试的公平性。

    @Testpublic void prototype() throws CloneNotSupportedException {QuestionBankController questionBankController = new QuestionBankController();System.out.println(questionBankController.createPaper("花花", "1000001921032"));System.out.println(questionBankController.createPaper("豆豆", "1000001921051"));System.out.println(questionBankController.createPaper("大宝", "1000001921987"));}
// 题库管理
public class QuestionBankController {private QuestionBank questionBank = new QuestionBank();public QuestionBankController() {questionBank.append(new ChoiceQuestion("JAVA所定义的版本中不包括", new HashMap<String, String>() {{put("A", "JAVA2 EE");put("B", "JAVA2 Card");put("C", "JAVA2 ME");put("D", "JAVA2 HE");put("E", "JAVA2 SE");}}, "D")).append(new ChoiceQuestion("下列说法正确的是", new HashMap<String, String>() {{put("A", "JAVA程序的main方法必须写在类里面");put("B", "JAVA程序中可以有多个main方法");put("C", "JAVA程序中类名必须与文件名一样");put("D", "JAVA程序的main方法中如果只有一条语句,可以不用{}(大括号)括起来");}}, "A")).append(new ChoiceQuestion("变量命名规范说法正确的是", new HashMap<String, String>() {{put("A", "变量由字母、下划线、数字、$符号随意组成;");put("B", "变量不能以数字作为开头;");put("C", "A和a在java中是同一个变量;");put("D", "不同类型的变量,可以起相同的名字;");}}, "B")).append(new ChoiceQuestion("以下()不是合法的标识符", new HashMap<String, String>() {{put("A", "STRING");put("B", "x3x;");put("C", "void");put("D", "de$f");}}, "C")).append(new ChoiceQuestion("表达式(11+3*8)/4%3的值是", new HashMap<String, String>() {{put("A", "31");put("B", "0");put("C", "1");put("D", "2");}}, "D")).append(new AnswerQuestion("小红马和小黑马生的小马几条腿", "4条腿")).append(new AnswerQuestion("铁棒打头疼还是木棒打头疼", "头最疼")).append(new AnswerQuestion("什么床不能睡觉", "牙床")).append(new AnswerQuestion("为什么好马不吃回头草", "后面的草没了"));}public String createPaper(String candidate, String number) throws CloneNotSupportedException {QuestionBank questionBankClone = (QuestionBank) questionBank.clone();questionBankClone.setCandidate(candidate);questionBankClone.setNumber(number);return questionBankClone.toString();}}
// 题库
public class QuestionBank implements Cloneable{private String candidate; // 考生private String number;    // 考号private ArrayList<ChoiceQuestion> choiceQuestionList = new ArrayList<>();private ArrayList<AnswerQuestion> answerQuestionList = new ArrayList<>();public QuestionBank append(ChoiceQuestion choiceQuestion) {choiceQuestionList.add(choiceQuestion);return this;}public QuestionBank append(AnswerQuestion answerQuestion) {answerQuestionList.add(answerQuestion);return this;}@Overridepublic Object clone() throws CloneNotSupportedException {QuestionBank questionBank = (QuestionBank) super.clone();questionBank.choiceQuestionList = (ArrayList<ChoiceQuestion>) choiceQuestionList.clone();questionBank.answerQuestionList = (ArrayList<AnswerQuestion>) answerQuestionList.clone();// 题目乱序Collections.shuffle(questionBank.choiceQuestionList);Collections.shuffle(questionBank.answerQuestionList);// 答案乱序ArrayList<ChoiceQuestion> choiceQuestionList = questionBank.choiceQuestionList;for (ChoiceQuestion question : choiceQuestionList) {Topic random = TopicRandomUtil.random(question.getOption(), question.getKey());question.setOption(random.getOption());question.setKey(random.getKey());}return questionBank;}public void setCandidate(String candidate) {this.candidate = candidate;}public void setNumber(String number) {this.number = number;}@Overridepublic String toString() {StringBuilder detail = new StringBuilder("考生:" + candidate + "\r\n" +"考号:" + number + "\r\n" +"--------------------------------------------\r\n" +"一、选择题" + "\r\n\n");for (int idx = 0; idx < choiceQuestionList.size(); idx++) {detail.append("第").append(idx + 1).append("题:").append(choiceQuestionList.get(idx).getName()).append("\r\n");Map<String, String> option = choiceQuestionList.get(idx).getOption();for (String key : option.keySet()) {detail.append(key).append(":").append(option.get(key)).append("\r\n");;}detail.append("答案:").append(choiceQuestionList.get(idx).getKey()).append("\r\n\n");}detail.append("二、问答题" + "\r\n\n");for (int idx = 0; idx < answerQuestionList.size(); idx++) {detail.append("第").append(idx + 1).append("题:").append(answerQuestionList.get(idx).getName()).append("\r\n");detail.append("答案:").append(answerQuestionList.get(idx).getKey()).append("\r\n\n");}return detail.toString();}}
// 选择题
public class ChoiceQuestion {private String name;                 // 题目private Map<String, String> option;  // 选项;A、B、C、Dprivate String key;                  // 答案;Bpublic ChoiceQuestion() {}public ChoiceQuestion(String name, Map<String, String> option, String key) {this.name = name;this.option = option;this.key = key;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Map<String, String> getOption() {return option;}public void setOption(Map<String, String> option) {this.option = option;}public String getKey() {return key;}public void setKey(String key) {this.key = key;}
}
// 解答题
public class AnswerQuestion {private String name;  // 问题private String key;   // 答案public AnswerQuestion() {}public AnswerQuestion(String name, String key) {this.name = name;this.key = key;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getKey() {return key;}public void setKey(String key) {this.key = key;}
}

考生:花花
考号:1000001921032
--------------------------------------------
一、选择题

第1题:表达式(11+3*8)/4%3的值是
A:2
B:0
C:31
D:1
答案:A

第2题:变量命名规范说法正确的是
A:变量不能以数字作为开头;
B:不同类型的变量,可以起相同的名字;
C:变量由字母、下划线、数字、$符号随意组成;
D:A和a在java中是同一个变量;
答案:A

第3题:下列说法正确的是
A:JAVA程序中类名必须与文件名一样
B:JAVA程序的main方法必须写在类里面
C:JAVA程序的main方法中如果只有一条语句,可以不用{}(大括号)括起来
D:JAVA程序中可以有多个main方法
答案:B

第4题:以下()不是合法的标识符
A:de$f
B:void
C:STRING
D:x3x;
答案:B

第5题:JAVA所定义的版本中不包括
A:JAVA2 HE
B:JAVA2 EE
C:JAVA2 SE
D:JAVA2 ME
E:JAVA2 Card
答案:A

二、问答题

第1题:小红马和小黑马生的小马几条腿
答案:4条腿

第2题:为什么好马不吃回头草
答案:后面的草没了

第3题:铁棒打头疼还是木棒打头疼
答案:头最疼

第4题:什么床不能睡觉
答案:牙床


考生:豆豆
考号:1000001921051
--------------------------------------------
一、选择题

第1题:下列说法正确的是
A:JAVA程序的main方法必须写在类里面
B:JAVA程序中类名必须与文件名一样
C:JAVA程序中可以有多个main方法
D:JAVA程序的main方法中如果只有一条语句,可以不用{}(大括号)括起来
答案:A

第2题:以下()不是合法的标识符
A:de$f
B:STRING
C:x3x;
D:void
答案:D

第3题:表达式(11+3*8)/4%3的值是
A:2
B:0
C:1
D:31
答案:A

第4题:变量命名规范说法正确的是
A:A和a在java中是同一个变量;
B:变量不能以数字作为开头;
C:不同类型的变量,可以起相同的名字;
D:变量由字母、下划线、数字、$符号随意组成;
答案:B

第5题:JAVA所定义的版本中不包括
A:JAVA2 Card
B:JAVA2 EE
C:JAVA2 HE
D:JAVA2 ME
E:JAVA2 SE
答案:C

二、问答题

第1题:小红马和小黑马生的小马几条腿
答案:4条腿

第2题:为什么好马不吃回头草
答案:后面的草没了

第3题:铁棒打头疼还是木棒打头疼
答案:头最疼

第4题:什么床不能睡觉
答案:牙床


考生:大宝
考号:1000001921987
--------------------------------------------
一、选择题

第1题:以下()不是合法的标识符
A:x3x;
B:STRING
C:de$f
D:void
答案:D

第2题:JAVA所定义的版本中不包括
A:JAVA2 ME
B:JAVA2 EE
C:JAVA2 HE
D:JAVA2 SE
E:JAVA2 Card
答案:C

第3题:变量命名规范说法正确的是
A:变量由字母、下划线、数字、$符号随意组成;
B:不同类型的变量,可以起相同的名字;
C:A和a在java中是同一个变量;
D:变量不能以数字作为开头;
答案:D

第4题:下列说法正确的是
A:JAVA程序的main方法必须写在类里面
B:JAVA程序中类名必须与文件名一样
C:JAVA程序的main方法中如果只有一条语句,可以不用{}(大括号)括起来
D:JAVA程序中可以有多个main方法
答案:A

第5题:表达式(11+3*8)/4%3的值是
A:2
B:1
C:0
D:31
答案:A

二、问答题

第1题:小红马和小黑马生的小马几条腿
答案:4条腿

第2题:为什么好马不吃回头草
答案:后面的草没了

第3题:什么床不能睡觉
答案:牙床

第4题:铁棒打头疼还是木棒打头疼
答案:头最疼

十一、单例模式

单例(Singleton)模式的定义:指一个类只有一个实例,且该类能自行创建这个实例的一种模式。例如,Windows 中只能打开一个任务管理器,这样可以避免因打开多个任务管理器窗口而造成内存资源的浪费,或出现各个窗口显示内容的不一致等错误。

单例模式有 3 个特点:

  1. 单例类只有一个实例对象;
  2. 该单例对象必须由单例类自行创建;
  3. 单例类对外提供一个访问该单例的全局访问点;

懒汉式单例

该模式的特点是类加载时没有生成单例,只有当第一次调用 getlnstance 方法时才去创建这个单例。代码如下:

public class LazySingleton
{private static volatile LazySingleton instance=null;    //保证 instance 在所有线程中同步private LazySingleton(){}    //private 避免类在外部被实例化public static synchronized LazySingleton getInstance(){//getInstance 方法前加同步if(instance==null){instance=new LazySingleton();}return instance;}
}

 饿汉式单例

该模式的特点是类一旦加载就创建一个单例,保证在调用 getInstance 方法之前单例已经存在了。

public class HungrySingleton
{private static final HungrySingleton instance=new HungrySingleton();private HungrySingleton(){}public static HungrySingleton getInstance(){return instance;}
}

饿汉式单例在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以是线程安全的,可以直接用于多线程而不会出现问题。

应用实例

用懒汉式单例模式模拟产生美国当今总统对象。

public class SingletonLazy
{public static void main(String[] args){President zt1=President.getInstance();zt1.getName();    //输出总统的名字President zt2=President.getInstance();zt2.getName();    //输出总统的名字if(zt1==zt2){System.out.println("他们是同一人!");}else{System.out.println("他们不是同一人!");}}
}
class President
{private static volatile President instance=null;    //保证instance在所有线程中同步//private避免类在外部被实例化private President(){System.out.println("产生一个总统!");}public static synchronized President getInstance(){//在getInstance方法上加同步if(instance==null){instance=new President();}else{System.out.println("已经有一个总统,不能产生新总统!");}return instance;}public void getName(){System.out.println("我是美国总统:特朗普。");}  
}
产生一个总统!
我是美国总统:特朗普。
已经有一个总统,不能产生新总统!
我是美国总统:特朗普。
他们是同一人!

单例模式的应用场景

以下是它通常适用的场景的特点。

在应用场景中,某类只要求生成一个对象的时候,如一个班中的班长、每个人的身份证号等。

当对象需要被共享的场合。由于单例模式只允许创建一个对象,共享该对象可以节省内存,并加快对象访问速度。如 Web 中的配置对象、数据库的连接池等。

当某类需要频繁实例化,而创建的对象又频繁被销毁的时候,如多线程的线程池、网络连接池等。

十二、适配器模式

适配器模式(Adapter Pattern)将某个类的接口转换成客户端期望的另一个接口表示,主的目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作。也称包装器(Wrapper),属于结构型模式,可以理解为原本不兼容的接口,通过适配修改做到统一。

工作原理
将一个类的接口转换成另一种接口,让原本接口不兼容的类可以兼容。从用户的角度看不到被适配者,是解耦的。用户调用适配器转化出来的目标接口方法,适配器再调用被适配者的相关接口方法,用户收到反馈结果,感觉只是和目标接口交互。

简单来说,适配器模式就像个插头转换器,让不同标准的插头和插座可以一起使用,而插座就是原来的接口,插头是用户期望的接口。或者类比电源适配器,把原来的220V电压转换成5V电压等。

//被适配的类
public class Voltage220V {public int output220V() {int src = 220;System.out.println("电压=" + src);return src;}
}//适配接口
public interface IVoltage5V {public int output5V();
}//适配器类
public class VoltageAdapter extends Voltage220V implements IVoltage5V {@Overridepublic int output5V() {// TODO Auto-generated method stub//获取220V电压int srcV = output220V();int dstV = srcV / 44 ; //转成5vreturn dstV;}
}//调用
public class Phone {public void charging(IVoltage5V iVoltage5V) {if(iVoltage5V.output5V() == 5) {System.out.println("电压是5v,可以充电");	} else if (iVoltage5V.output5V() > 5) {System.out.println("电压大于5v,不可充电");}}
}//测试
public class Client {public static void main(String[] args) {System.out.println("===类适配器测试===");Phone phone = new Phone();phone.charging(new VoltageAdapter());}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/4395.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

回溯-单词搜索

给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 单词必须按照字母顺序&#xff0c;通过相邻的单元格内的字母构成&#xff0c;其中“相邻”单元格是那些水平相邻或垂直相…

SpringMVC深解--一起学习吧之架构

SpringMVC的工作原理主要基于请求驱动&#xff0c;它采用了前端控制器模式来进行设计。以下是SpringMVC工作原理的详细解释&#xff1a; 请求接收与分发&#xff1a; 当用户发送一个请求到Web服务器时&#xff0c;这个请求首先会被SpringMVC的前端控制器&#xff08;Dispatche…

关于远程桌面端口的优化措施的建议

在信息技术的世界中&#xff0c;远程桌面连接已成为企业、教育和个人用户之间共享信息、协作工作的重要工具。而这一切的背后&#xff0c;都离不开远程桌面端口&#xff08;RDP&#xff0c;Remote Desktop Protocol Port&#xff09;的支持。RDP端口不仅关乎到远程访问的顺畅性…

【Linux】进程信号 -- 详解

⚪前言 注意&#xff1a;进程间通信中的信号量跟下面要讲的信号没有任何关系。 一、从不同角度理解信号 1、生活角度的信号 你在网上买了很多件商品&#xff0c;在等待不同商品快递的到来。但即便快递没有到来&#xff0c;你也知道快递来临时&#xff0c;你该怎么处理快递&a…

CDA认证:数据行业领跑者,告别危机和低谷!

近日&#xff0c;人力资源社会保障部、中央组织部、中央网信办、国家发展改革委、教育部、科技部、工业和信息化部、财政部、国家数据局等九部门印发《加快数字人才培育支撑数字经济发展行动方案&#xff08;2024—2026年&#xff09;》&#xff08;以下简称《行动方案》&#…

LeetCode39题: 组合总和(原创)

【题目描述】 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target &#xff0c;找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 &#xff0c;并以列表形式返回。你可以按 任意顺序 返回这些组合。candidates 中的 同一个 数字可以 无限制重复…

[NISACTF 2022]bilala的二维码

​​​​​​​NSSCTF{M0RS34ND282X231} 还有一个是像素我找不到

Orange3数据可视化(树查看器-决策树)

树视图 分类和回归树的可视化。 输入 树&#xff1a;决策树 输出 选中的数据&#xff1a;从树节点中选中的实例 数据&#xff1a;带有额外一列&#xff0c;显示每个点是否被选中 这是一个多功能的小部件&#xff0c;用于展示分类和回归树的2D可视化。用户可以选择一个节点…

物联网五层架构:每一层都扮演着不可或缺的角色——青创智通

物联网五层架构涵盖了感知层、网络层、数据层、应用层和业务层&#xff0c;每一层都扮演着不可或缺的角色&#xff0c;共同构成了物联网的完整生态系统。下面我们将详细探讨这五层架构的功能和特点。 首先&#xff0c;感知层是物联网的起点&#xff0c;负责获取和识别各种物理世…

ssm项目后端如何导出war及前端如何导出静态资源

后端如何导出war包 后端工具&#xff1a;IDEA 2020.1.3 运行我们编写工具maven里面的package 运行成功的日志 我们运行完&#xff0c;会生成一个target文件夹&#xff0c;在这个文件夹里面找到war包即可 前端如何导出静态资源 使用工具&#xff1a;WebStorm 2020.1.3 打开左…

VUE3核心语法

&#x1f49f;&#x1f49f;前言 ​ 友友们大家好&#xff0c;我是你们的小王同学&#x1f617;&#x1f617; 今天给大家打来的是 VUE3核心语法 希望能给大家带来有用的知识 觉得小王写的不错的话麻烦动动小手 点赞&#x1f44d; 收藏⭐ 评论&#x1f4c4; 小王的主页&#xf…

数据结构习题--旋转链表

数据结构习题–旋转链表 给你一个链表的头节点 head &#xff0c;旋转链表&#xff0c;将链表每个节点向右移动 k 个位置。注意这里的k可能超过链表的长度 方法&#xff1a;双指针 分析 旋转K次&#xff0c;我们其实就是相当于找到倒数第K个结点&#xff0c;让其成为头结点…

基于Springboot的考研资讯平台

基于SpringbootVue的考研资讯平台的设计与实现 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringbootMybatis工具&#xff1a;IDEA、Maven、Navicat 系统展示 用户登录 首页 考研资讯 报考指南 资料信息 论坛信息 后台登录 考研资讯管理 学生管理 资…

学pyhton的第二十二天

原文链接&#xff1a;Python 图形化界面设计&#xff08;Tkinter&#xff09; - 简书 (jianshu.com) 相关博客链接 接第十八天Tkinter的内容&#xff1a; 单选按钮&#xff08;控件&#xff1a;Radiobutton&#xff09;&#xff1a; 除共有属性外&#xff0c;还具有显示文本…

算法学习笔记Day9——动态规划基础篇

一、介绍 本文解决几个问题&#xff1a;动态规划是什么&#xff1f;解决动态规划问题有什么技巧&#xff1f;如何学习动态规划&#xff1f; 1. 动态规划问题的一般形式就是求最值。动态规划其实是运筹学的一种最优化方法&#xff0c;只不过在计算机问题上应用比较多&#xff…

opencv_17_翻转与旋转

一、图像翻转 1&#xff09;void flip_test(Mat& image); 2&#xff09;void ColorInvert::flip_test(Mat& image) { Mat dst; //flip(image, dst, 0); //上下翻转 flip(image, dst, 1); //左右翻转 // flip(image, dst, -1); //180度翻转 imsho…

OpenVINO安装教程 vcpkg版

通过 vcpkg 安装 OpenVINO™ Runtime 请注意&#xff0c;vcpkg 发行版&#xff1a; 仅提供 C/C API 不支持 NPU 推理 专用于所有主要操作系统的用户&#xff1a;Windows、Linux 和 macOS &#xff08;所有 x86_64 / ARM64 架构&#xff09; 系统要求处理器说明软件要求 完整…

B站美化插件,支持自定义,太酷辣~

大公司的软件和网站通常具有优雅的默认界面设计。 以国内二次元聚集地B站为例&#xff0c;可以说它的UI设计非常吸引人。与其他视频网站繁复的设计相比&#xff0c;B站的界面设计可以说是遥遥领先 然而&#xff0c;总有些人对默认的用户界面感到不满意&#xff0c;他们渴望尝试…

fiddler提示“The system proxy was changed.Click to reenable capturing.”的原因及解决办法

一、现象描述 运行fiddler时&#xff0c;提示“The system proxy was changed.Click to reenable capturing.”&#xff0c;即使点击了该提示&#xff0c;重新进行抓包&#xff0c;不一会儿&#xff0c;该提示又会出现&#xff0c;又不能进行抓包了。 二、解决方法 2.1 原因一&…

输电线路的“天眼”:双目协同图像视频监测装置

在广袤的天地之间&#xff0c;纵横交错的输电线路如同血脉一般&#xff0c;为我们的生活输送着源源不断的电力。然而&#xff0c;这些“血脉”也常常面临着各种挑战&#xff0c;如外力破坏、恶劣天气等。为了守护这些重要的“生命线”&#xff0c;鼎信智慧研发了一款智能监控设…