將實際執行的服務遮蔽,取而代之的,建立一個代理人負責對外窗口的身份,以及對內與該服務溝通。
我們可能會依照情況的需求,必須將實際運作服務的物件進行遮蔽,這些情況可能是:
為了實現以上的需求,需要建立一個代理人,負責對外窗口的一切,任何需求都會先經過代理人這一關,通過這關後才會與實際服務接觸、運作等等。
實踐的作法是:
以下範例以需求「服務本身接觸到機密資料,不能開放給外部任意存取」為核心製作。
使用者物件:User
public class User {
private String id;
private String name;
private String level;
public User(String id, String name, String level) {
this.id = id;
this.name = name;
this.level = level;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
public String getLevel() {
return level;
}
}
定義共同方法的虛擬層:AccessDataLibrary
public interface AccessDataLibrary {
HashMap<String, User> operatorUsers(String token);
HashMap<String, User> auditUsers(String toke);
HashMap<String, User> adminUsers(String token);
}
實際服務的物件:AccessData
public class AccessData implements AccessDataLibrary {
private HashMap<String, User> adminData = new HashMap<>();
private HashMap<String, User> auditData = new HashMap<>();
private HashMap<String, User> operatorData = new HashMap<>();
public AccessData() throws IOException, ParseException {
for (int i = 0; i < 3; i++) {
String id = RandomID.exec(5);
String name = NameSelector.exec();
adminData.put(name, new User(id, name, "admin"));
}
for (int i = 0; i < 5; i++) {
String id = RandomID.exec(7);
String name = NameSelector.exec();
auditData.put(name, new User(id, name, "audit"));
}
for (int i = 0; i < 10; i++) {
String id = RandomID.exec(10);
String name = NameSelector.exec();
operatorData.put(name, new User(id, name, "operator"));
}
}
@Override
public HashMap<String, User> operatorUsers(String token) {
return operatorData;
}
@Override
public HashMap<String, User> auditUsers(String token) {
return auditData;
}
@Override
public HashMap<String, User> adminUsers(String token) {
return adminData;
}
}
代理人物件:AccessDataProxy
(Proxy 物件)
public class AccessDataProxy implements AccessDataLibrary {
private AccessData accessData;
public AccessDataProxy() throws IOException, ParseException {
this.accessData = new AccessData();
}
@Override
public HashMap<String, User> operatorUsers(String token) {
if (token.contentEquals("operator") || token.contentEquals("audit") || token.contentEquals("admin")) {
return accessData.operatorUsers("PASS");
} else {
return new HashMap<>();
}
}
@Override
public HashMap<String, User> auditUsers(String token) {
if (token.contentEquals("audit") || token.contentEquals("admin")) {
return accessData.auditUsers("PASS");
} else {
return new HashMap<>();
}
}
@Override
public HashMap<String, User> adminUsers(String token) {
if (token.contentEquals("admin")) {
return accessData.adminUsers("PASS");
} else {
return new HashMap<>();
}
}
}
使用介面:AccessDataApp
public class AccessDataApp {
private AccessDataLibrary middleware;
public AccessDataApp(AccessDataLibrary middleware) {
this.middleware = middleware;
}
public void pushLaunchButton(User user, String target) {
System.out.println("身為 " + user.getLevel() + ",嘗試取得 " + target + " 等級的資料");
HashMap<String, User> targetData = null;
if (target.contentEquals("operator")) {
targetData = middleware.operatorUsers(user.getLevel());
} else if (target.contentEquals("audit")) {
targetData = middleware.auditUsers(user.getLevel());
} else if (target.contentEquals("admin")) {
targetData = middleware.adminUsers(user.getLevel());
}
System.err.println("\n---系統顯示---");
if (targetData == null) {
System.err.println("權限不符合");
} else {
System.out.println("人員姓名: " + user.getName() + " ,身份: " + user.getLevel() + " ,順利取得 " + target + " 等級的資料");
System.out.println("人員清單:");
for (User listUser : targetData.values()) {
System.out.println(listUser.getId() + " " + listUser.getName() + " " + listUser.getLevel());
}
}
System.err.println("---系統關閉---\n");
}
}
測試,建立不同權限的使用者取得不同等級的資料:AccessDataProxySample
public class AccessDataProxySample {
public static void main(String[] args) throws IOException, ParseException {
AccessDataLibrary accessDataProxy = new AccessDataProxy();
AccessDataApp app = new AccessDataApp(accessDataProxy);
System.out.println("模擬不同權限的人員,取得資料的情境");
System.out.println("情境一:Operator");
User operator = new User(RandomID.exec(5), NameSelector.exec(), "operator");
app.pushLaunchButton(operator, "admin");
app.pushLaunchButton(operator, "audit");
app.pushLaunchButton(operator, "operator");
System.out.println("情境二:Audit");
User audit = new User(RandomID.exec(5), NameSelector.exec(), "audit");
app.pushLaunchButton(audit, "admin");
app.pushLaunchButton(audit, "audit");
app.pushLaunchButton(audit, "operator");
System.out.println("情境三:Admin");
User admin = new User(RandomID.exec(5), NameSelector.exec(), "admin");
app.pushLaunchButton(admin, "admin");
app.pushLaunchButton(admin, "audit");
app.pushLaunchButton(admin, "operator");
}
}
Utils:NameSelector
、RandomID
public class NameSelector {
private static Random random = new Random();
public static String exec() throws IOException, ParseException {
JSONParser parser = new JSONParser();
Object obj = parser.parse(new FileReader("./src/utils/boyNameList.json")); // 讀取 JSON Array,內容是字串陣列
JSONArray jsonArray = (JSONArray) obj;
// 0 - 149
int option = random.nextInt(149 + 0) + 0;
return (String) jsonArray.get(option);
}
}
public class RandomID {
private static final Random RANDOM = new SecureRandom();
private static final String ALPHABET = "0123456789QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm";
private RandomID() {
throw new IllegalStateException("Utility class");
}
public static String exec(int length) {
StringBuilder buffer = new StringBuilder(length);
for (int i = 0; i < length; i++) {
buffer.append(ALPHABET.charAt(RANDOM.nextInt(ALPHABET.length())));
}
return new String(buffer);
}
}
使用者物件:User
class User {
/**
* @param {string} id
* @param {string} name
* @param {string} level
*/
constructor(id, name, level) {
this.id = id;
this.name = name;
this.level = level;
}
getId() {
return this.id;
}
getName() {
return this.name;
}
getLevel() {
return this.level;
}
}
定義共同方法的虛擬層:AccessDataLibrary
/** @interface */
class AccessDataLibrary {
/**
* @param {string} token
* @returns {Map<string, User>}
*/
operatorUsers(token) {
return new Map();
}
/**
* @param {string} token
* @returns {Map<string, User>}
*/
auditUsers(token) {
return new Map();
}
/**
* @param {string} token
* @returns {Map<string, User>}
*/
adminUsers(token) {
return new Map();
}
}
實際服務的物件:AccessData
class AccessData extends AccessDataLibrary {
constructor() {
super();
/** @type {Map<string, User>} */
this.adminData = new Map();
/** @type {Map<string, User>} */
this.auditData = new Map();
/** @type {Map<string, User>} */
this.operatorData = new Map();
for (let i = 0; i < 3; i++) {
const id = randomID(5);
const name = nameSelector();
this.adminData.set(name, new User(id, name, "admin"));
}
for (let i = 0; i < 5; i++) {
const id = randomID(7);
const name = nameSelector();
this.auditData.set(name, new User(id, name, "audit"));
}
for (let i = 0; i < 10; i++) {
const id = randomID(10);
const name = nameSelector();
this.operatorData.set(name, new User(id, name, "operator"));
}
}
/**
* @override
* @param {string} token
* @returns {Map<string, User>}
*/
operatorUsers(token) {
return this.operatorData;
}
/**
* @override
* @param {string} token
* @returns {Map<string, User>}
*/
auditUsers(token) {
return this.operatorData;
}
/**
* @override
* @param {string} token
* @returns {Map<string, User>}
*/
adminUsers(token) {
return this.operatorData;
}
}
代理人物件:AccessDataProxy
(Proxy 物件)
class AccessDataProxy extends AccessDataLibrary {
constructor() {
super();
this.accessData = new AccessData();
}
/**
* @override
* @param {string} token
* @returns {Map<string, User>}
*/
operatorUsers(token) {
if (token === "operator" || token === "audit" || token === "admin") {
return this.accessData.operatorUsers("PASS");
} else {
return new Map();
}
}
/**
* @override
* @param {string} token
* @returns {Map<string, User>}
*/
auditUsers(token) {
if (token === "audit" || token === "admin") {
return this.accessData.auditUsers("PASS");
} else {
return new Map();
}
}
/**
* @override
* @param {string} token
* @returns {Map<string, User>}
*/
adminUsers(token) {
if (token === "admin") {
return this.accessData.adminUsers("PASS");
} else {
return new Map();
}
}
}
使用介面:AccessDataApp
class AccessDataApp {
/**
* @param {AccessDataLibrary} middleware
*/
constructor(middleware) {
this.middleware = middleware;
}
/**
* @param {User} user
* @param {string} target
*/
pushLaunchButton(user, target) {
console.log("身為 " + user.getLevel() + ",嘗試取得 " + target + " 等級的資料");
/** @type {Map<string, User>} */
let targetData = null;
if (target === "operator") {
targetData = this.middleware.operatorUsers(user.getLevel());
} else if (target === "audit") {
targetData = this.middleware.auditUsers(user.getLevel());
} else if (target === "admin") {
targetData = this.middleware.adminUsers(user.getLevel());
}
console.error("\n---系統顯示---");
if (targetData == null) {
console.error("權限不符合");
} else {
console.log("人員姓名: " + user.getName() + " ,身份: " + user.getLevel() + " ,順利取得 " + target + " 等級的資料");
console.log("人員清單:");
for (const listUser of targetData.values()) {
console.log(listUser.getId() + " " + listUser.getName() + " " + listUser.getLevel());
}
}
console.error("---系統關閉---\n");
}
}
測試,建立不同權限的使用者取得不同等級的資料:AccessDataProxySample
const accessDataProxySample = () => {
const accessDataProxy = new AccessDataProxy();
const app = new AccessDataApp(accessDataProxy);
console.log("模擬不同權限的人員,取得資料的情境");
console.log("情境一:Operator");
const operator = new User(randomID(5), nameSelector(), "operator");
app.pushLaunchButton(operator, "admin");
app.pushLaunchButton(operator, "audit");
app.pushLaunchButton(operator, "operator");
console.log("情境二:Audit");
const audit = new User(randomID(5), nameSelector(), "audit");
app.pushLaunchButton(audit, "admin");
app.pushLaunchButton(audit, "audit");
app.pushLaunchButton(audit, "operator");
console.log("情境三:Admin");
const admin = new User(randomID(5), nameSelector(), "admin");
app.pushLaunchButton(admin, "admin");
app.pushLaunchButton(admin, "audit");
app.pushLaunchButton(admin, "operator");
}
accessDataProxySample();
Utils:NameSelector
、RandomID
/**
* @param {number} digits
*/
const randomID = (digits) => {
const ALPHABET = "0123456789QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm";
let id = "";
for (let i = 0; i < digits; i++) {
const randomIndex = Math.floor(Math.random() * (ALPHABET.length - 1 - 0 + 1)) + 0;
id += ALPHABET[randomIndex];
}
return id;
}
const nameSelector = () => {
const boyNameList = require('./src/utils/boyNameList.json'); // 讀取 JSON Array,內容是字串陣列
const option = Math.floor(Math.random() * (149 - 0 + 1)) + 0;
return boyNameList[option];
}
Proxy 模式好玩在於,閱讀到常見情境時,才恍然大悟,不少情境自己早已體驗過。
真的很有趣,在於我從來沒想過這些情境原來都可以稱作 Proxy 模式!
這是最後一篇 Structural patterns,明天將進入下個類別:Behavioural patterns 的第一個模式:Chain of Responsibility 模式。