争对
对象/类
创建时的优化
通过定义顶层抽象工厂类,通过继承的方式,针对于每一个产品都提供一个工厂类用于创建。 情况:只适用于简单对象,当我们需要生产许多个产品族的时候,这种模式就有点乏力了
创建对象不再使用传统的new,而是创建一个工厂类,作为all实体类创建对象的一个封装类。(避免了更改类名、构造方法时,需要修改大量的代码)
简单工厂模式:(不灵活,不建议)
不符合开闭原则。如果输入没有提前写好的水果则就需要再添加每个类里的代码
//水果抽象类
public abstract class Fruit {
private final String name;
public Fruit(String name){
this.name = name;
}
@Override
public String toString() {
return name+"@"+hashCode(); //打印一下当前水果名称,还有对象的hashCode
}
}
//水果实体类
public class Apple extends Fruit{ //苹果,继承自水果
public Apple() {
super("苹果");
}
}
public class Orange extends Fruit{ //橘子,也是继承自水果
public Orange() {
super("橘子");
}
}
//水果工厂
public class FruitFactory {
/**
* 这里就直接来一个静态方法根据指定类型进行创建
* @param type 水果类型
* @return 对应的水果对象
*/
public static Fruit getFruit(String type) {
switch (type) {
case "苹果":
return new Apple();
case "橘子":
return new Orange();
default:
return null;
}
}
}
//主方法
public class Main {
public static void main(String[] args) {
Fruit fruit = FruitFactory.getFruit("橘子"); //直接问工厂要,而不是我们自己去创建
System.out.println(fruit);
}
}
工厂方法模式:通过范型灵活实现
如果新增了水果类型,直接创建一个新的工厂类就行,不需要修改之前已经编写好的内容。 缺点:一种水果就有一种新的工厂类,太多工厂类了
//水果抽象类
public abstract class Fruit {
private final String name;
public Fruit(String name){
this.name = name;
}
@Override
public String toString() {
return name+"@"+hashCode(); //打印一下当前水果名称,还有对象的hashCode
}
}
//水果工厂
public abstract class FruitFactory<T extends Fruit> { //将水果工厂抽象为抽象类,添加泛型T由子类指定水果类型
public abstract T getFruit(); //不同的水果工厂,通过此方法生产不同的水果
}
//Apple工厂
public class AppleFactory extends FruitFactory<Apple> { //苹果工厂,直接返回Apple,一步到位
@Override
public Apple getFruit() {
return new Apple();
}
}
//主方法
public class Main {
public static void main(String[] args) {
test(new AppleFactory()::getFruit); //比如我们现在要吃一个苹果,那么就直接通过苹果工厂来获取苹果
}
//此方法模拟吃掉一个水果
private static void test(Supplier<Fruit> supplier){
System.out.println(supplier.get()+" 被吃掉了,真好吃。");
}
}
情况:适用于有一系列产品的公司。 缺点:容易违背开闭原则。一旦增加了一种产品,此时就必须去修改抽象工厂的接口,这样就涉及到抽象工厂类的以及所有子类的改变
举例:
实际上这些产品都是成族出现的,比如小米的产品线上有小米12,小米平板等,华为的产品线上也有华为手机、华为平板,但是如果按照我们之前工厂方法模式来进行设计,那就需要单独设计9个工厂来生产上面这些产品,显然这样就比较浪费时间的。
我们就可以使用抽象工厂模式,我们可以将多个产品,都放在一个工厂中进行生成,按不同的产品族进行划分,比如小米,那么我就可以安排一个小米工厂,而这个工厂里面就可以生产整条产品线上的内容,包括小米手机、小米平板、小米路由等。
//工厂抽象类
public abstract class AbstractFactory {
public abstract Phone getPhone();
public abstract Table getTable();
public abstract Router getRouter();
}
//工厂实现类
public class AbstractFactoryImpl extends AbstractFactory{
@Override
public Phone getPhone() {
return new ProductPhone();
}
@Override
public Table getTable() {
return new ProductTable();
}
@Override
public Router getRouter() {
return new ProductRouter();
}
}
//产品抽象类
public abstract class AbRouter{
public abstract Router getRouter();
}
...
//产品实体类
public class Router extends AbRouter{
@Override
public Router getRouter(){
return new Router();
}
}
当构造对象时参数较多,可以通过建造者模式使用链式方法创建对象,保证参数填写正确。
可以去看看StringBuilder的源码,有很多的框架都为我们提供了形如XXXBuilder
的类型,我们一般也是使用这些类来创建我们需要的对象。
建造者模式创建对象其实和StringBuilder
一样:实际上我们是通过建造者来不断配置参数或是内容,当我们配置完所有内容后,最后再进行对象的构建。
public static void main(String[] args) {
StringBuilder builder = new StringBuilder(); //创建一个StringBuilder来逐步构建一个字符串
builder.append(666); //拼接一个数字
builder.append("老铁"); //拼接一个字符串
builder.insert(2, '?'); //在第三个位置插入一个字符
System.out.println(builder.toString()); //差不多成形了,最后转换为字符串
}
举例:
//实体类的编写
public class Student {
int id;
int age;
int grade;
String name;
String college;
String profession;
List<String> awards;
//一律使用建造者来创建,不对外直接开放
private Student(int id, int age, int grade, String name, String college, String profession, List<String> awards) {
this.id = id;
this.age = age;
this.grade = grade;
this.name = name;
this.college = college;
this.profession = profession;
this.awards = awards;
}
public static StudentBuilder builder(){ //通过builder方法直接获取建造者
return new StudentBuilder();
}
public static class StudentBuilder{ //这里就直接创建一个内部类
//Builder也需要将所有的参数都进行暂时保存,所以Student怎么定义的这里就怎么定义
int id;
int age;
int grade;
String name;
String college;
String profession;
List<String> awards;
public StudentBuilder id(int id){ //直接调用建造者对应的方法,为对应的属性赋值
this.id = id;
return this; //为了支持链式调用,这里直接返回建造者本身,下同
}
public StudentBuilder age(int age){
this.age = age;
return this;
}
...
public StudentBuilder awards(String... awards){
this.awards = Arrays.asList(awards);
return this;
}
public Student build(){ //最后我们只需要调用建造者提供的build方法即可根据我们的配置返回一个对象
return new Student(id, age, grade, name, college, profession, awards);
}
}
}
//主方法
public static void main(String[] args) {
Student student = Student.builder() //获取建造者
.id(1) //逐步配置各个参数
.age(18)
.grade(3)
.name("小明")
.awards("ICPC-ACM 区域赛 金牌", "LPL 2022春季赛 冠军")
.build(); //最后直接建造我们想要的对象
}
单例模式:在计算机进程中,同一个类始终只会有一个对象来进行操作。
多例模式:在计算机进程中,对一个实体类创建一次对象就是对当个对象操作,若是创建多个对象则是分别对对应的对象操作。
单例模式的三种写法:
定义:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。(说白了就是复制)
public static void main(String[] args) {
int a = 10;
int b = a; //基本类型浅拷贝
System.out.println(a == b); //true
Object o = new Object();
Object k = o; //引用类型浅拷贝,拷贝的仅仅是对上面对象的引用
System.out.println(o == k); //true
}
使用Cloneable接口提供的拷贝机制,来实现原型模式:操作完会发现Object的clone
默认还是浅复制
protected class Student implements Cloneable{ //注意需要实现Cloneable接口
...
//Cloneable中的方法,下面代码复制Object的clone源码
@Override
public Object clone() throws CloneNotSupportedException { //提升clone方法的访问权限
return super.clone();
}
}
//主方法
public static void main(String[] args) throws CloneNotSupportedException {
Student student0 = new Student();
Student student1 = (Student) student0.clone();
System.out.println(student0);
System.out.println(student1);
//两个结果不同,就是地址不同
Student student0 = new Student("小明");
Student student1 = (Student) student0.clone();
System.out.println(student0.getName() == student1.getName());
//true
}
深拷贝:在student实现接口Cloneable后重写clone方法
@Override
public Object clone() throws CloneNotSupportedException { //这里我们改进一下,针对成员变量也进行拷贝
Student student = (Student) super.clone();
student.name = new String(name);
return student; //成员拷贝完成后,再返回
}