Java面試--設計模式
設計模式
單例模式【Singleton】
定義:確保全域性最多隻有一個物件
適用:構建緩慢的物件;需要統一管理的資源
優點:1.減少記憶體的佔用 2.單例模式會阻止其他物件例項化其自己的單例物件的副本,從而確保所有物件都訪問唯一例項。 3.因為類控制了例項化過程,所以類可以靈活更改例項化過程
缺點:1.很多全域性狀態 2. 執行緒安全性問題
建立:
1.雙重鎖模式【詳解】
將同步內容下放到if內部,提高了執行的效率,不必每次獲取物件時都進行同步,只有第一次才同步,建立了以後就沒必要了
2.作為 Java 類的靜態變數
3.使用框架提供的能力
變繼承關係為組合關係
繼承關係
1.描述 is-a 的關係
2.不要用繼承關係來實現複用,要使用設計模式來實現複用
例 將Employee 變為 Manager
程式碼:state(狀態)模式【詳解】
Role.java
package interview.designpattern.company;
public interface Role {
void doWork();
}
Engineer.java
package interview.designpattern.company; public class Engineer implements Role { @Override public void doWork() { System.out.println("Doing engineer work."); } @Override public String toString() { return "Engineer"; } }
Employee.java
package interview.designpattern.company; import java.util.List; import java.util.Objects; public class Employee { public static List<Employee> allEmployees; private final String name; private final int salary; private Role role; public Employee(String name, int salary, Role role) { this.name = name; this.salary = salary; this.role = role; } public void doWork() { //新增程式碼 role.doWork(); } public void getPaid(BankEndPoint bank) { bank.payment(name, salary); } // Package private for logic in the package to control // when employees are loaded. static void loadAllEmployees() { // Loads all employees from database. } @Override public int hashCode() { return Objects.hash(name, salary, role); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } Employee other = (Employee) obj; //新增程式碼 return Objects.equals(this.name, other.name) && Objects.equals(this.salary, other.salary) && Objects.equals(this.role, other.role); } @Override public String toString() { return "Employee [name=" + name + ", salary=" + salary + ", role=" + role + "]"; } public String getName() { return name; } public int getSalary() { return salary; } public Role getRole() {//新增程式碼 return role; } public void setRole(Role role) {//新增程式碼 this.role = role; } }
Tester.java
package interview.designpattern.company;
import java.util.Arrays;
import java.util.LinkedList;
public class Tester {
public static void main(String[] args) {
Employee employee1 = new Employee("John", 10000,
new Engineer());
Employee employee2 = new Employee("Mary", 20000,
new Engineer());
LinkedList<Employee> employees = new LinkedList<>();
employees.add(employee1);
employees.add(employee2);
System.out.println("Print using for each");
for (Employee employee : employees) {
System.out.println(employee);
}
System.out.println("Testing managers");
employee2.setRole(new Manager(Arrays.asList(employee1)));
for (Employee employee : employees) {
System.out.println(employee);
}
System.out.println("Testing doWork");
System.out.println("Employee1");
employee1.doWork();
System.out.println("Employee2");
employee2.doWork();
}
}
輸出:
Decorator模式【詳解】
裝飾(Decorator)模式又名包裝(Wrapper)模式。裝飾模式以對客戶端透明 的方式擴充套件物件的功能,是繼承關係的一個替代方案。
裝飾模式以對客戶透明的方式動態地給一個物件附加上更多的責任。換言之,客戶端並不會覺得物件在裝飾前和裝飾後有什麼不同。裝飾模式使用原來被裝飾的類的一個子類的例項,把客戶端的呼叫委派到被裝飾類。關鍵在於這種擴充套件是完全透明的。與生成子類相比,它更具有靈活性。
LogginRunnable 和 CodingTask 都要滿足 Runnable 介面
CodingTask 是真正做事的,LogginRunnable 是裝飾
程式碼:LoggingRunnable.java
package interview.designpattern.task;
public class LoggingRunnable implements Runnable {
private final Runnable innerRunnable;
public LoggingRunnable(Runnable innerRunnable) {
this.innerRunnable = innerRunnable;
}
@Override
public void run() {
long startTime = System.currentTimeMillis();
System.out.println("Task started at "
+ startTime);
innerRunnable.run();
System.out.println("Task finished. Elapsed time: "
+ (System.currentTimeMillis() - startTime));
}
}
CodingTask.java
package interview.designpattern.task;
public class CodingTask implements Runnable {
private final int employeeId;
public CodingTask(int employeeId) {
this.employeeId = employeeId;
}
@Override
public void run() {
System.out.println("Employee " + employeeId
+ " started writing code.");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("Employee " + employeeId
+ " finished writing code.");
}
}
TransactionalRunnable.java
package interview.designpattern.task;
public class TransactionalRunnable implements Runnable {
private final Runnable innerRunnable;
public TransactionalRunnable(Runnable innerRunnable) {
this.innerRunnable = innerRunnable;
}
@Override
public void run() {
boolean shouldRollback = false;
try {
beginTransaction();
innerRunnable.run();
} catch (Exception e) {
shouldRollback = true;
throw e;
} finally {
if (shouldRollback) {
rollback();
} else {
commit();
}
}
}
private void commit() {
System.out.println("commit");
}
private void rollback() {
System.out.println("rollback");
}
private void beginTransaction() {
System.out.println("beginTransaction");
}
}
Tester.java
package interview.designpattern.task;
public class Tester {
public static void main(String[] args) {
new LoggingRunnable(
new TransactionalRunnable(
new CodingTask(0))).run();
}
}
輸出:
如何建立物件
使用 new 來建立的缺點:
1.編譯時必須決定建立那個類的物件
2.引數意義不明確
解決方法:
抽象工廠(Abstract Factory)模式【詳解】
抽象工廠模式(Abstract Factory Pattern)是一種比較常用的模式,其定義如下:
Provide an interface for creating families of related or dependent objects without specifying their concrete classes. 即為建立一組相關或相互依賴的物件提供一個介面,而且無須指定它們的具體類。它的通用類圖如下:
1)封裝性。每個產品的實現類不是高層模組要關心的,它要關心的是介面,是抽象,它不關心物件是如何創建出來的,這都由工廠類負責的,只要知道工廠類是誰,我就能建立一個需要的物件,省時省力。
2)產品族內的約束為非公開狀態。例如生產男女比例的問題上,猜想女媧娘娘肯定有自己的打算,那麼在抽象工廠模式中,這些約束都在工廠內裡面實現的。
缺點
抽象工廠模式最大的缺點就是產品族擴充套件非常困難。如果我們要增加一個產品C,也就是說產品族由原來的A和B增加到3個,那麼我們首先要在抽象類AbstractCreator中增加createProductC()方法,然後兩個實現類都要修改……說到這裡,已經知道了擴充套件的弊端了……
注意這裡是產品族擴充套件比較困難,而不是產品等級擴充套件困難。產品等級擴充套件還是非常容易的,增加一個產品等級,只要增加一個工廠類負責新增加出來的產品生產任務即可。也就是說橫向擴充套件容易,縱向擴充套件難。