1. 程式人生 > >關於簡單工廠模式與開閉原則的討論(1)

關於簡單工廠模式與開閉原則的討論(1)

說個老生常談的問題的了,就是簡單工廠模式到底是不是違背了開閉原則?

在討論之前,先重溫下什麼是開閉原則。

開閉原則(open/closed principle)的定義是,software entities (classes, modules, functions) should be open for extension, but closed for modification,一個類開放它的拓展,關閉它的修改,即一個類可以允許拓展它的功能,而不允許修改它的原始碼。

先舉個簡單的例子,關於開閉原則的,看下面一段程式碼

class DefenseBuff
{
        private int m_buffType;

        public int BuffType
        {
            get { return m_buffType; }
            set { m_buffType = value; }
        }

        public int GetDiscountDamage(int TotalDamage)
        {
                if (m_buffType == 1)
                {
                    return TotalDamage - 100;
                }
                else
                {
                    return TotalDamage - 50;
                }
        }
}
上面的問題在於,如果策劃要加一個buff種類,你必須又加一個if-else的條件判斷,這樣會造成無休止的修改此類,然後再次測試此類能否正常執行,包括引用到它的類又能否正常執行。如果到後期系統越變越龐大,則非常難維護。

這就是違背了開閉原則。

如果我們改成下面這樣:用繼承和多型的方式,如果加了新的型別,我們只要建立一個新的子類就行了,這樣就不會違背開閉原則

class DefenseBuff
{
        public virtual int GetDiscountDamage(int TotalDamage)
        {
            return TotalDamage;
        }
}

class SilverDefenseBuff : DefenseBuff
{
	public override int GetDiscountDamage(int TotalDamage)
	{
		return base.GetDiscountDamage(TotalDamage) - 50;
	}
}

class GoldDefenseBuff : DefenseBuff
{
	public override int GetDiscountDamage(int TotalDamage)
	{
		return base.GetDiscountDamage(TotalDamage) - 100;
	}
}
或者我們可以重Excel資料表中讀資料,然而在現實開發中,策劃一般把數值寫在Excel表中的,不會向上面那樣寫死的,然而我們也不需要再修改程式碼,而策劃可以任意修改數值和增加型別。

class DefenseBuff
{
	private int m_buffType;

	public int BuffType
	{
		get { return m_buffType; }
		set { m_buffType = value; }
	}
    public DefenseBuff(int buffType)
	{
		m_buffType = buffType;
	}
	public int GetDiscountDamage(int TotalDamage)
	{
		return BuffTable.GetType(m_buffType).DiscountDamage;//從excel配置表讀取資料
	}
}
var player = WorldMap.Instance.GetSelfPlayer();
Monster m = WorldMap.Instance.GetMonsterPool().GetAt(0);
DefenseBuff bf = new DefenseBuff(PlayerProperty.Instance.GetMetaData(player.ID).buffType);
player.Damage(bf.GetDiscountDamage(MonsterTable.GetType(m.ID)).AttackDamage));

說完了開閉原則,現在來說一下簡單工廠模式,看下面程式碼:

using System;

enum MonsterType
{
	SKELETON,
	GHOUST,
	EVIL,
	CACODEMON
}

abstract class Monster
{
	public abstract int AttackDamage { get; }
}
	
class Skeleton : Monster
{
	public override int AttackDamage
	{
		get
		{
			return 10;
		}
		
	}
}

class Ghoust : Monster
{
	public override int AttackDamage
	{
		get
		{
			return 20;
		}
	}
}

class Evil : Monster
{
	public override int AttackDamage
	{
		get
		{
			return 50;
		}
	}
}

class Cacodemon : Monster
{
	public override int AttackDamage
	{
		get
		{
			return 100;
		}
	}
}

static class MonsterFactory
{
	public static Monster Create(MonsterType monster_type)
	{
		switch(monster_type)
		{
			case MonsterType.SKELETON:
				return new Skeleton();
			case MonsterType.GHOUST:
				return new Ghoust();
			case MonsterType.EVIL:
				return new Evil();
			case MonsterType.CACODEMON:
				return new Cacodemon();
		}
	}
}


var player = WorldMap.Instance.GetSelfPlayer();
var monster = MonsterFactory.Create(MonsterType.SKELETON);
player.Damage(monster.AttackDamage);
上述程式碼的問題就在於,如果策劃要加一個新的怪物類,那我們就必須增加一個Swith-Case條件,就必須修改MonsterFactory的內部程式碼,這個其實在一定程度上也已經違背了開閉原則

如果改成下面的方式,類似於抽象工廠的方式,如果新加了一個怪物類,我們只要新增一個MonsterInfo的子類即可,就不會違背開閉原則了。

public abstract class MonsterInfo
{
    public abstract Monster CreateMonster();
}

public class SkeletonInfo : MonsterInfo
{
    public override Skeleton CreateMonster()
    {
        return new Skeleton();
    }
}

public class GhoustInfo : MonsterInfo
{
	public override Ghoust CreateMonster()
	{
		return new Ghoust();
	}
}

public static class MonsterFactory
{
    public static Monster Create(MonsterInfo aInfo)
    {       
        return aInfo.CreateMonster();
    }
}
var player = WorldMap.Instance.GetSelfPlayer();
var monster = MonsterFactory.Create(new SkeletonInfo());
player.Damage(monster.AttackDamage);
或者我們使用泛型,會更簡潔方便
static class MonsterFactory<T> where T : Monster,new()
{
	public static Monster Create()
	{
		return new T();
	}
}
var player = WorldMap.Instance.GetSelfPlayer();
var monster = MonsterFactory<Skeleton>.Create();
player.Damage(monster.AttackDamage);

或者使用反射的方式:
public static class MonsterFactory
{
	public static Monster Create(Type monsterType)
	{
		return Activator.CreateInstance(monsterType) as Monster;
	}
	
	public static Monster Create(string monsterType)
	{
		Type type = Assembly.GetExecutingAssembly().GetType(monsterType);
        return Activator.CreateInstance(type) as Monster;
	}
}
var player = WorldMap.Instance.GetSelfPlayer();
var monster1 = MonsterFactory.Create(typeof(SkeletonInfo));
var monster2 = MonsterFactory.Create("Ghoust");
player.Damage(monster1.AttackDamage);
player.Damage(monster2.AttackDamage);

綜上所述,我們基本可以認為帶過多的switch-case或if-else的簡單工廠模式是違背開閉原則的,會增加程式碼的耦合度,會造成當系統變得龐大時,極其難以維護。

其實我們有很多的方法,可以繞開過多swith-case或if-else的條件,以滿足開閉原則來實現簡單的工廠模式。

關於更多的工廠模式和開閉原則,我們今後再繼續討論,今天先說這麼多。如果有什麼錯誤或不對的,歡迎大家多多指正。



相關推薦

關於簡單工廠模式原則的討論(1)

說個老生常談的問題的了,就是簡單工廠模式到底是不是違背了開閉原則? 在討論之前,先重溫下什麼是開閉原則。 開閉原則(open/closed principle)的定義是,software entities (classes, modules, functions) sho

【設計模式】六大原則之一(單一職責原則

【前言】         最近在學習設計模式,設計模式是面向物件程式設計的。設計模式有6大原則,而實際上都是互補的,也就是說一些原則需要利用另一些原則來實現自己。下面來簡單的介紹一下六大原則其中之二 【單一職責原則】 1、單一職責原則的由來         初學者在程式設計

簡單工廠模式工廠方法模式

interface ace nal tor sim str bre sys 對象比較 簡單工廠 定義:簡單工廠模式屬於創建型模式,其又被稱為靜態工廠方法模式,這是由一個工廠對象決定創建出哪一種產品類的實例 工廠類 public class CamputerFacto

【設計模式簡單工廠模式工廠方法模式詳細解讀

1.前言  設計模式,各具特色,今天在此總結一下常見模式中 簡單工廠模式與工廠方法模式的區別與聯絡 2.兩種模式概要    1. 簡單工廠模式     2. 工廠方法模式   工廠方法模式又稱

淺談設計模式簡單工廠模式策略模式

本文主要是關於面向物件程式設計(OOP)的設計模式,瞭解優秀軟體設計的演變過程比學習優秀的設計本身更有價值,因為設計演變的過程蘊藏著巨大的智慧,是一代一代程式設計師的思想的結晶。通過學習設計模式,設計出易於維護、擴充套件、複用以及靈活性好的程式;使用多型、繼承、

android 二級快取(單一原則原則

import android.content.Context; import android.graphics.Bitmap; import java.io.IOException; /** * SD卡快取 */ public class DiskCache imple

簡單工廠模式工廠方法模式以及Mybatis中工廠模式的使用

一、簡單工廠模式 Java是一門面向物件的語言。有很多時候我們需要去生產物件,工廠模式就是我們生產物件的一種方式。 比如這樣一個場景: 我們需要做一個兩個數之間的運算,但我們不知道使用者究竟要使用哪種運算子對其進行運算。我們就可以使用工廠模式,對使用者的選擇做一個判斷,從

設計模式原則

override cati 聖誕 turn extension span 核心 inter open 開放封閉原則(Open Close Principle,簡稱OCP)盡量通過擴展軟件實體的行為來實現變化,而不是通過修改已有的代碼來實現變化 原始定義:Software

工廠模式OO設計原則

如果把建立看作一個職責,那麼系統中的哪個物件應該擁有這個職責呢?如果把建立看作知識,那麼建立知識應該放置在什麼地方呢?說到職責我們不得不說一下著名的GRASP原則: GRASP是通用職責分配軟體模式(General Responsibility Assignment Soft

一、Java設計模式原則

定義:一個軟體實體,如類、模組和函式應該對擴充套件開放,對修改關閉 用抽象構建框架,用實現擴充套件細節 優點:提高軟

設計模式之軟體開發原則1原則和依賴倒置原則

開閉原則 定義 所謂開閉原則就是一個軟體實體如類、模組和函式應該對擴充套件開放、對修改關閉。 強呼叫抽象構建框架,實現實現拓展細節。 有優點是提高軟體的複用性和易維護展性。是面向物件的最基本原則。 依賴倒置原則 定義 高層模組不應該依賴底層模組,二者都應該依賴其抽象。 抽象不應該依賴細節:細節應該

軟體架構模式(依賴注入 控制反轉 依賴倒置原則 原則 單一職責原則 介面隔離原則 里氏代換原則)

名詞解釋:   依賴:  一個獨立元素的變化會影響到相關的元素   派生:  一個類是由其他類衍生出的,子類繼承了基類的結構(屬性的名詞和型別)方法   抽象:  去掉每個不重要的細節,專

大話設計模式之四:1~5章(簡單工廠模式 、策略模式、單一職責原則、開放封閉原則 、依賴倒轉原則

注:《大話設計模式》這本書很好的介紹了設計模式,其對應的原始碼是C#語言寫得,跑在visual studio上,所以自己先安裝visual studio ,然後將原始碼跑一跑,這樣能深刻的理解《大話設

設計模式六大原則(6):原則

思考 外部 編程人員 恰恰 單一職責 何事 適應 擴展 分享 開閉原則 定義:一個軟件實體如類、模塊和函數應該對擴展開放,對修改關閉。 問題由來:在軟件的生命周期內,因為變化、升級和維護等原因需要對軟件原有代碼進行修改時,可能會給舊代碼中引入錯誤,也可能會使我們不得不對

設計模式筆記1簡單工廠模式

1.3 簡單 修改 作用 面向對象 對象 面向 tro 計算   如果想成為一名更優秀的軟件設計師,了解優秀軟件設計的演變過程比學習優秀設計本身更有價值。 1.1 面向對象的好處   通過封裝、繼承多態把程序的耦合度降低,使用設計模式使得程序更加靈活,容易修改,易於復用

初識設計模式1簡單工廠模式

簡單工廠 height 判斷 目前 mes strong 產品 return logs 簡單工廠模式 簡單工廠模式是類的創建模式,又叫做靜態工廠方法模式。簡單工廠模式由一個工廠對象決定生產出哪一種產品類的實例。 為什麽要使用簡單工廠模式 原因很簡單:解耦。 LOL場

11設計模式六大原則——原則

職責 art 並不是 錯誤 接口 屬於 倒置 編程 探討 定義:一個軟件實體如類、模塊和函數應該對擴展開放,對修改關閉。 問題由來:在軟件的生命周期內,因為變化、升級和維護等原因需要對軟件原有代碼進行修改時,可能會給舊代碼中引入錯誤,也可能會使我們不得不對整個功能進行重構,

《JAVA模式》之簡單工廠模式

pue hgj ans vfs 創建對象 mdm gho fdm got 在閻宏博士的《JAVA與模式》一書中開頭是這樣描述簡單工廠模式的:簡單工廠模式是類的創建模式,又叫做靜態工廠方法(Static Factory Method)模式。簡單工廠模式是由一個工廠對象決定創建

一(1簡單工廠模式

ner log factory auto 過多 測試 extend 耦合度 工廠 1 設計模式中經常用到程序設計中的封裝,繼承,多態的思想。不同的設計模式使代碼更加靈活,容易擴展。 2 設計模式中一般都會存在父類的概念,父類一般是抽象類,內部維護的變量,用於子類繼承;維護的

接口簡單工廠模式

接口 簡單工廠模式 接口:使用interface關鍵字聲明接口。例如:接口需要被類實現,因為接口只做聲明不做實現。例如:Implements是實現接口關鍵字,一旦類實現了某個接口,就必須重寫這個接口聲明的方法。接口會有一個符號顯示著:看到這個符號就可以知道這是一個接口。聲明了接口就必須應用接口編程,不