设计模式——创建型
更新: 12/31/2024 字数: 0 字 时长: 0 分钟
思想
在Java基础中我们学过,如果我们想要获取一个类的实例, 只需要使用构造方法new一个出来, 然而在实际业务开发中,这一方式存在很多的缺点 (比如多个类密切相关,我们只需要在不同的情况下切换接口输入的类来更改具体参数,这时可能需要多个if-else), 为了优化这些缺点,创建型设计模式诞生了,
创建型设计模式通过使用接口+类的组合通过使用一个中间类返回不同的接口实现类来实现不同情况下使用方法的区分。
工厂模式
业务场景
对于一个业务,我们可能存在多种情况,不同的情况跟据不同的参数区分,在这里我们有一种方法,就是将这些业务全部塞在一个类的方法中。
public ResponseVO response(Integer n){
if(n==1){
return ResponseVO.success(new ObjectA()); //业务A
} else if (n == 2) {
return ResponseVO.success(new ObjectB()); //业务B
}else if (n==3){
return ResponseVO.success(new ObjectC()); //业务C
}else {
return ResponseVO.failure("failure"); //错误信息
}
}
在这种情况下我们的代码全部塞在一个方法中,过于不清晰,且在未来的维护过程中很有可能随着业务的升级和增加,if-else的内容会变得更长,在这种情况下,我们就需要考虑使用工厂设计模式
实现
我们可以将每个if的代码块抽离出来,然后定义一个接口,接着跟据每个不同的情景创建不同的类去实现这个接口,这样我们就完成了类——接口的对应, 然后我们再次定义一个factory类,通过factory类的方法来决定我们使用哪个类,并把类返回回去。 这样我们在业务层就只需要同共工厂类获取这个接口,然后调用接口的方法即可。
工厂类
public class StoreFactory {
public ICommodity getCommodityService(Integer commodityType) {
if (null == commodityType) return null;
if (1 == commodityType) return new CouponCommodityService(); //具体类,这个类需要实现ICommodity接口
if (2 == commodityType) return new GoodsCommodityService();
if (3 == commodityType) return new CardCommodityService();
throw new RuntimeException("不存在的商品服务类型");
}
}
使用
public class Main {
public static StoreFactory storeFactory=new StoreFactory();
public static void main(String[] args) throws Exception {
ICommodity commodityService = storeFactory.getCommodityService(1);
commodityService.sendCommodity("1","1","1",new HashMap<>()); //sendCommodity就是ICommodity接口的方法,我们的每个返回类都实现了这个方法。
}
}
抽象工厂模式
业务场景
在前文中我们讲述的工厂模式是跟据传参的不同将一个接口的不同实现类通过工厂类返回给我们的业务层。 但是在这种情况下又会出现一个新的问题, 如果我们想要返回多个不同的类/接口该如何解决?
这个时候我们就需要使用抽象工厂模式。
抽象工厂模式本身并不关心你的类的具体实现,只是希望通过你的这个抽象工厂得到一个可以使用的类。 这时我们就可以使用抽象工厂方法,将获取不同接口的逻辑进一步拆分到抽象工厂的实现类中完成。
实现
抽象工厂接口
public interface AbstractFactory {
DetailDao getDetailDao(); //除了这样直接区分外,也可以搞一个方法,在方法里实现复杂的区分逻辑
MainDao getMainDao();
}
抽象工厂实现
public class AbstractFactoryImpl implements AbstractFactory {
@Override
public DetailDao getDetailDao() {
return new DetailDaoImpl();
//这里可以再引入一个工厂模式,用来区分何时返回什么样的DetailDao的实现类
//这也是为什么我们有时会说抽象工厂是工厂的工厂
}
@Override
public MainDao getMainDao() {
return new MainDaoImpl();
}
}
单例模式
业务场景
这个模式十分好理解,因为我们再SpringBoot中使用的Bean就是单例模式。
在我们的业务中,除了实体类以外还有业务类,这种类里面实现了我们业务的解决逻辑, 但是和实体类不同,业务类往往是一成不变的,我们不需要多个不同的业务类。 这个时候再通过new来创建类就显得不大合适了。
因此我们此处引入单例模式来解决这个问题。
实现
这里我们给出Effective Java作者推荐的枚举方式解决单例模式
public enum Singleton {
INSTANCE;
private String siName ="Singleton";
public void fun1(){
System.out.println("hi~");
}
public void fun2(boolean flag){
if(flag){
System.out.println(true);
}else {
System.out.println(false);
}
}
public String fun3(){
return "Singleton";
}
public String getSiName(){
return siName;
}
//.....
}
public class ApiTest {
public Singleton instance=Singleton.INSTANCE; //从枚举中获取单例
@Test
public void test() {
instance.fun1();
instance.fun2(true);
System.out.println(instance.fun3());
String siName = instance.getSiName();
}
}
建造者模式
业务场景
建造者模式也十分常见,我们使用的StringBuilder就是建造者模式, 除此之外LomBook中也内置了Builder的生成方法, Builder可以通过定义创建方法的可以通过定义Builder方法的形式决定哪些参数是必要的
原型模式
业务场景
在业务中存在一种情况,我们的一个类存在多个相同的部分 (比如五个参数,三个都相同,或者三个都是通过一系列固定参数生成出的参数), 在这种请款下我们创建类的时候往往会出现创建太多重复部分的情况。
因此我们需要使用原型模式。
原型模式实现了一个Cloneable类(或者也可以自定义一个接口,但核心思想是该接口的实现是对该类的拷贝), 然后使用该类先创建一次,这次创建的过程中定义出那些所有难创建且高重复的值, 接着再使用clone方法clone这个类(克隆出的类成为clone类),接着对clone类传入他独特的参数, 然后再将这个clone类返回回去,同时保留原本被clone的类用于下次的clone。
实现
public class QuestionBankController {
private QuestionBank questionBank = new QuestionBank();
public QuestionBankController() {
Map<String, String> map01 = new HashMap<String, String>();
map01.put("A", "JAVA2 EE");
map01.put("B", "JAVA2 Card");
map01.put("C", "JAVA2 ME");
map01.put("D", "JAVA2 HE");
map01.put("E", "JAVA2 SE");
Map<String, String> map02 = new HashMap<String, String>();
map02.put("A", "JAVA程序的main方法必须写在类里面");
map02.put("B", "JAVA程序中可以有多个main方法");
map02.put("C", "JAVA程序中类名必须与文件名一样");
map02.put("D", "JAVA程序的main方法中如果只有一条语句,可以不用{}(大括号)括起来");
Map<String, String> map03 = new HashMap<String, String>();
map03.put("A", "变量由字母、下划线、数字、$符号随意组成;");
map03.put("B", "变量不能以数字作为开头;");
map03.put("C", "A和a在java中是同一个变量;");
map03.put("D", "不同类型的变量,可以起相同的名字;");
Map<String, String> map04 = new HashMap<String, String>();
map04.put("A", "STRING");
map04.put("B", "x3x;");
map04.put("C", "void");
map04.put("D", "de$f");
Map<String, String> map05 = new HashMap<String, String>();
map05.put("A", "31");
map05.put("B", "0");
map05.put("C", "1");
map05.put("D", "2");
questionBank.append(new ChoiceQuestion("JAVA所定义的版本中不包括", map01, "D"))
.append(new ChoiceQuestion("下列说法正确的是", map02, "A"))
.append(new ChoiceQuestion("变量命名规范说法正确的是", map03, "B"))
.append(new ChoiceQuestion("以下()不是合法的标识符",map04, "C"))
.append(new ChoiceQuestion("表达式(11+3*8)/4%3的值是", map05, "D"))
.append(new AnswerQuestion("小红马和小黑马生的小马几条腿", "4条腿"))
.append(new AnswerQuestion("铁棒打头疼还是木棒打头疼", "头最疼"))
.append(new AnswerQuestion("什么床不能睡觉", "牙床"))
.append(new AnswerQuestion("为什么好马不吃回头草", "后面的草没了"));
}
//上面一整个函数都是在创建questionBank,这样复杂的创建明显不适合我们每创建一个实体类就单独走一遍
public String createPaper(String candidate, String number) throws CloneNotSupportedException {
QuestionBank questionBankClone = (QuestionBank) questionBank.clone(); //实现cloneable接口
questionBankClone.setCandidate(candidate);接下来的操作就是对cloneable类中哪些不固定的值进行传入
questionBankClone.setNumber(number);
return questionBankClone.toString();
}
}