- The concept of DRY is super simple - do not duplicate codes if there are already in the program. The basic practice is using a function to do a particular action and let other places call the function if they need.
// bad const name1 = 'Bob' const msg1 = 'Hi' console.log(`${name1} says: ${msg1}`) const name2 = 'Tom' const msg2 = `How's it going?` console.log(`${name2} says: ${msg2}`) // good function say(name, message) { console.log(`${name} says: ${message}`) } say('Bob', 'Hi') say('Tom', `How's it going?`) - However, once the requirement changed and we modify the function, of course, all function calls will be affected. That's good we save time.
function say(from, to, message) { console.log(`${from} says ${to}: ${message}`) } say('Bob', 'John', 'Hi') say('Tom', 'Peter', `How's it going?`) - It is very common that we use parameters as inputs to let a function deal with for different situations.
function say(from, to, message) { if (to !== null && to !== undefined) { console.log(`${from} says ${to}: ${message}`) } else { console.log(`${from} says: ${message}`) } } say('Bob', 'John', 'Hi') say('Tom', null, `How's it going?`) - You know, the requirement might change many times because customers are indecisive. Even if they don't, we still have to do refactoring in order to have better code quality. That means we might change codes.
function say(from, to, message, isQuestion) { const ending = isQuestion ? '?' : '.' if (to !== null && to !== undefined) { console.log(`${from} says ${to}: ${message}${ending}`) } else { console.log(`${from} says: ${message}${ending}`) } } say('Bob', null, 'Hi', false) say('Tom', 'Peter', `How's it going`, true) - You will realize that functions have more complex parameters and conditions are very hard to modify. It calls "high coupling" if there are too many places rely on a heavy code.
// the combinations you need to test in order to make sure your function works: say('name', null, 'message', false) say('name', 'name', 'message', false) say('name', null, 'message', true) say('name', 'name', 'message', true) // the combinations you may not expect that will ruin your function: say(null, 'name', 'message', false) say('name', 'name', null, true) - Therefore, when you are trying to DRY your code, be careful that don't put too many codes into one function. The function will be super complex and hard to maintain. It may be a good idea to have small functions instead.
// there are actually 4 scenarios, so it may be a good idea to have 4 functions // new function names are more semantic // there are no magical boolean flag and optional parameters (reduce parameter mistakes) // we get rid of if conditions (reduce the complexity) // once one of the functions changed, we won't affect other functions function talk(name, message) { console.log(`${name} says: ${message}.`) } function askQuestion(name, message) { console.log(`${name} says: ${message}?`) } function talkToSomeone(name, someone, message) { console.log(`${name} says to ${someone}: ${message}.`) } function askQuestionToSomeone(name, someone, message) { console.log(`${name} says to ${someone}: ${message}?`) } talk('Bob', 'Hi') askQuestion('Tom', `How's it going`) talkToSomeone('Bob', 'John', 'Hi') askQuestionToSomeone('Tom', 'Peter', `How's it going`) - This example is overly simple, and the naming actually sucks, but I hope I give you a general idea of how to organize your functions.
2019年7月23日 星期二
Reuse, but don't make a huge function
2013年5月11日 星期六
一秒看破 裝飾者模式 Decorator Pattern
設計模式 (design patterns) 有人覺得很難 有人覺得很神秘
其實他是一種 在特定情境下 使用一些別具巧思的物件導向技巧 的方法
往往直接看定義說明 根本難以理解到底在幹嘛
那不如直接回歸原點 一開始就從[情境]來看看怎麼玩
這回要來玩弄的是 裝飾者模式 (Decorator Pattern)
先拜讀神人文
神人文 其實已經說明得很清楚了 小弟我也不必在此 裝很懂
我的心得是:
當有一群 [本質相近]但又個有些[不盡相同]的東西 的時候 適合使用 裝飾者模式
那就來玩一下吧 我們這麼宅 又覺得 不吃早餐才是一件很嘻哈的事
所以神人舉例的早餐 改成我們熟悉的東東
弄個 武器 抽象類 跟 一些武器
//武器 抽象類
public abstract class Weapon
{
public string Name = "砂鍋大的鐵拳";
public virtual string GetName()
{
return Name;
}
public abstract double AttackPower();
}
//劍
public class Sword : Weapon
{
public Sword()
{
base.Name = "劍";
}
public override double AttackPower()
{
return 10;
}
}
//刀
public class Knife : Weapon
{
public Knife()
{
base.Name = "刀";
}
public override double AttackPower()
{
return 5;
}
}
再弄個 強化武器 抽象裝飾者類 跟 一些強化功能
//強化武器 抽象裝飾者類
public abstract class StrengthenDecorator : Weapon
{
public abstract override double AttackPower();
}
//加長
public class Long : StrengthenDecorator
{
private Weapon _weapon;
public Long(Weapon weapon)
{
_weapon = weapon;
}
public override double AttackPower()
{
return _weapon.AttackPower() + 5;
}
public override string GetName()
{
return "加長的" + _weapon.GetName();
}
}
//鋒利
public class Sharp : StrengthenDecorator
{
private Weapon _weapon;
public Sharp(Weapon weapon)
{
_weapon = weapon;
}
public override double AttackPower()
{
return _weapon.AttackPower() + 20;
}
public override string GetName()
{
return "鋒利的" + _weapon.GetName();
}
}
//劇毒
public class Toxic : StrengthenDecorator
{
private Weapon _weapon;
public Toxic(Weapon weapon)
{
_weapon = weapon;
}
public override double AttackPower()
{
return _weapon.AttackPower() + 100;
}
public override string GetName()
{
return "劇毒的" + _weapon.GetName();
}
}
//傳說
public class Legendary : StrengthenDecorator
{
private Weapon _weapon;
public Legendary(Weapon weapon)
{
_weapon = weapon;
}
public override double AttackPower()
{
return _weapon.AttackPower() + 9999;
}
public override string GetName()
{
return "傳說的" + _weapon.GetName();
}
}
最後 拉些 可有可無的 UI 看看效果
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private Weapon weapon;
private void Sword_rb_CheckedChanged(object sender, System.EventArgs e)
{
weapon = new Sword();
Log(weapon.GetName(),
weapon.AttackPower());
Knife_rb.Enabled = Sword_rb.Enabled
= false;
}
private void Knife_rb_CheckedChanged(object sender, System.EventArgs e)
{
weapon = new Knife();
Log(weapon.GetName(),
weapon.AttackPower());
Knife_rb.Enabled = Sword_rb.Enabled
= false;
}
private void GetLong_Click(object sender, System.EventArgs e)
{
weapon = new Long(weapon);
Log(weapon.GetName(),
weapon.AttackPower());
}
private void GetSharp_Click(object sender, System.EventArgs e)
{
weapon = new Sharp(weapon);
Log(weapon.GetName(),
weapon.AttackPower());
}
private void GetToxic_Click(object sender, System.EventArgs e)
{
weapon = new Toxic(weapon);
Log(weapon.GetName(),
weapon.AttackPower());
}
private void GetLegendary_Click(object sender, System.EventArgs e)
{
weapon = new Legendary(weapon);
Log(weapon.GetName(),
weapon.AttackPower());
}
private void Log(string name, double attackPower)
{
Log_lb.Text +=
"勇者OO拾取了 [" + name + "]," +
"攻擊力:" + attackPower + "。" +
Environment.NewLine;
}
}
定番截圖
訂閱:
文章 (Atom)