創建型設計模式
爭對
物件/類創建時的優化
工廠方法模式(了解)
通過定義頂層抽象工廠類,通過繼承的方式,針對于每一個產品都提供一個工廠類用于創建,
情況:只適用于簡單物件,當我們需要生產許多個產品族的時候,這種模式就有點乏力了
創建物件不再使用傳統的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 class Singleton { private final static Singleton INSTANCE = new Singleton(); //用于參考全域唯一的單例物件,在一開始就創建好 private Singleton() {} //禁用了構造方法Singleton()來創建物件,不允許隨便new,需要物件直接找getInstance public static Singleton getInstance(){ //獲取全域唯一的單例物件 return INSTANCE; } } -
加鎖的懶漢式單例(不建議,沒有第三種方法好)
懶漢:在要用的時候才創建物件,但又得防多執行緒就上了鎖
public class Singleton { private static volatile Singleton INSTANCE; //在一開始先不進行物件創建,volatile關鍵字是多執行緒的時候,這個變數更改了,別的執行緒可以立馬檢測到 private Singleton() {} //禁用了構造方法Singleton()來創建物件,不允許隨便new,需要物件直接找getInstance public static Singleton getInstance(){ if(INSTANCE == null) { //這層判斷是便于第一次外訪問時不用在走鎖 synchronized (Singleton.class) { //加鎖是為了防止多執行緒創建了多個物件 if(INSTANCE == null) INSTANCE = new Singleton(); //由于加了鎖,所以當一個行程進來創建了物件,其他執行緒需要再判斷一次有沒有人已經創建了這個類物件,有就不創建了,內層還要進行一次檢查,雙重檢查鎖定 } } return INSTANCE; } } -
靜態內部類的半懶、半餓式單例(建議)
靜態內部類該開始不會加載,需要的時候才會加載,由于這個類一加載就會創建物件,
所以實作了懶漢的資源不濫用,餓漢的防止多執行緒
public class Singleton { private Singleton() {}//禁用了構造方法Singleton()來創建物件,不允許隨便new,需要物件直接找getInstance private static class Holder { //由靜態內部類持有單例物件,但是根據類加載特性,我們僅使用Singleton類時,不會對靜態內部類進行初始化,一旦類初始化之后值將不會改變,有點餓漢式的味道, private final static Singleton INSTANCE = new Singleton(); } public static Singleton getInstance(){ //只有真正使用內部類時,才會進行類初始化 return Holder.INSTANCE; //直接獲取內部類中的 } }
原型模式
定義:用原型實體指定創建物件的種類,并且通過拷貝這些原型創建新的物件,(說白了就是復制)
- 淺拷貝:①對于類中基本資料型別,會直接復制值給拷貝物件;②對于參考型別(物件型別),只會復制物件的地址,而實際上指向的還是原來的那個物件,拷貝個基莫,
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; //成員拷貝完成后,再回傳
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/502039.html
標籤:設計模式
下一篇:設計模式之享元模式
