算法之道 2018-09-04
这是一篇关于设计模式的文章。 我们中的许多人使用策略以及工厂设计模式。 但是,有时候,很难区分两者之间的区别,并决定何时哪个更适合手头的项目。 让我们通过一个例子来看看每个模式的相应关键点。
针对策略设计模式,可通过以下关键点来加以理解:
● 策略设计模式是一种行为式设计模式,允许我们在运行时选择算法。 在此模式中,代码接收运行时指令以决定使用哪组算法。
● 策略设计模式使算法独立于使用它的上下文、客户端和代码而变化。
例如,对接收的数据执行验证的验证对象可以使用策略模式来选择验证算法,这取决于类型、数据源或基于其他用户参数。
这些验证算法将与验证对象分开编写,因此可以轻松地为不同的验证对象所使用,而无需任何代码重复。 任何验证对象也可以使用不同的验证算法,基于类型、来源或用户输入。
● 策略模式存储对某些算法或代码的引用,并在需要时才提供它。
简而言之,策略设计模式是设计模式族中许多已定义的算法之一,可应用于或用于数据。
决定在运行时使用哪种算法的能力,使得调用或客户端代码更灵活和可重用,并避免代码重复。
● 客户端或上下文不知道它必须使用哪种策略(算法)。
● 工厂设计模式是一种创造性设计模式,它提供了创建对象的最佳方法之一。 此模式使用工厂方法来处理创建对象的问题,而无需指定它必须创建的对象的确切类。
● 在工厂模式中,是通过调用工厂方法而不是通过调用构造函数来创建对象。
● 工厂模式是Java中最常用的设计模式之一。
● 对象创建逻辑是:
• 在接口中实现或由子类实现。
• 或者,它在基类中实现,并可选择由派生类重写。
● 在工厂设计模式中,可创建一个对象而不将创建逻辑暴露给客户端。
简而言之,工厂模式从我们可以使用的类族中提供适用的对象。此对象表示算法以及许多其他功能。
接下来,在下面的例子中,我将进一步尝试来强调两种模式的一些差异。 示例将保持其相对容易理解性,并严格关注编码风格。
在下文里,我使用银行账户管理的一个例子来分别实现讲解:
我们有各种策略来计算账户中存储的本金金额的利息金额。
所以,首先必须创建一个接口来定义策略(算法)的布局。这里创建InterestCalculationStrategy接口。代码如下:
package design.strategy; public interface InterestCalculationStrategy { double calculateInterest(double principal, double rate, int term);//计算利息 }
现在,我定义了两种利息计算逻辑或策略算法,算法实现类SimpleInterestCalculator,用于计算已定义利率和给定时期的简单的利息。代码如下:
package design.strategy; public class SimpleInterestCalculator implements InterestCalculationStrategy { @Override public double calculateInterest(final double principal, final double rate, final int term) {//本金、时期和利率 return ((principal * term * rate) / 100); } @Override public String toString() { return "Simple Interest"; } }
然后,使用CompoundInterestCalculator来计算已定义利率和给定时期的复合利息。代码如下:
package design.strategy; public class CompundInterestCalculator implements InterestCalculationStrategy { @Override public double calculateInterest(final double principal, final double rate, final int term) { return (principal * Math.pow(1.0 + rate/100.0, term) - principal); } @Override public String toString() { return "Compound Interest"; } }
我们有两种账户类型:储蓄或活期(Saving or Current )。 根据此账户类型定义,利率是固定的。 在这里,请注意我已根据账户类型确定了利率。代码如下:
package design.strategy; public enum AccountType { SAVING (2.0d), CURRENT (1.0d); private double rate; AccountType (final double rate) { this.rate = rate; } public double getRate() { return rate; } }
然后,我们生成一个模型对象来保存账户详细信息,如下所示:
package design.strategy; public class Account { private long accountNo;//账号 private String accountHolderName;//持有人 private AccountType accountType;//账号类型 private InterestCalculationStrategy interestStrategy;//利息算法 private double amount;//数额 public Account() { super(); } public Account(long accountNo, String accountHolderName, AccountType accountType) { this(); this.accountNo = accountNo; this.accountHolderName = accountHolderName; this.accountType = accountType; } public Account(long accountNo, String accountHolderName, AccountType accountType, InterestCalculationStrategy interestStrategy) { this(accountNo, accountHolderName, accountType); this.interestStrategy = interestStrategy; } public long getAccountNo() { return accountNo; } public void setAccountNo(long accountNo) { this.accountNo = accountNo; } public String getAccountHolderName() { return accountHolderName; } public void setAccountHolderName(String accountHolderName) { this.accountHolderName = accountHolderName; } public AccountType getAccountType() { return accountType; } public void setAccountType(AccountType accountType) { this.accountType = accountType; } public InterestCalculationStrategy getInterestStrategy() { return interestStrategy; } public void setInterestStrategy(InterestCalculationStrategy interestStrategy) { this.interestStrategy = interestStrategy; } public double getAmount() { return amount; } public void setAmount(double amount) { this.amount = amount; } public void deposit(double amount) {//存款 // 数额有效性检查 if (amount > 0.0d) { this.amount += amount; } } public void withdraw(double amount) {//取款 // 检查数额有效性以及账户金额的可用性 if (amount > 0.0d && amount < this.amount) { this.amount -= amount; } } public double getInterest(int term) { if (getInterestStrategy() != null && getAccountType() != null) { return getInterestStrategy().calculateInterest(getAmount(), getAccountType().getRate(), term); } return 0.0d; } @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("Account [accountNo=").append(getAccountNo()).append(", accountHolderName=") .append(getAccountHolderName()).append(", accountType=").append(getAccountType()).append(", rate=") .append((getAccountType() != null) ? getAccountType().getRate() : 0.0d).append(", interestStrategy=") .append(getInterestStrategy()).append(", amount=").append(getAmount()).append("]"); return builder.toString(); } }
现在,我们将通过以下代码测试策略模式:
package design.strategy; public class Main { public static void main(String[] args) { Account acct1 = new Account(12345678l, "Solo Cui", AccountType.SAVING); acct1.setInterestStrategy(new CompundInterestCalculator()); acct1.deposit(10000.0d); System.out.print(acct1); System.out.println(" has interest : " + acct1.getInterest(5)); Account acct2 = new Account(12345680l, "Killer X", AccountType.SAVING); acct2.setInterestStrategy(new SimpleInterestCalculator()); acct2.deposit(10000.0d); System.out.print(acct2); System.out.println(" has interest : " + acct2.getInterest(5)); } }
请注意这两个账户都是Saving类型的一部分,我们根据我们的选择使用不同的利息计算算法(Compound或Simple)。 基本上,算法可以与松散耦合的上下文(账户)一起使用。 这是使用策略设计模式的好处。
我们也可以在这里创建一个策略工厂(StrategyFactory),如下所示:
package design.strategy; public class StrategyFactory { public InterestCalculationStrategy createStrategy(String strategyType) { InterestCalculationStrategy strategy = null; if (strategyType != null) { if ("COMPOUND".equalsIgnoreCase(strategyType)) { strategy = new CompundInterestCalculator(); } else if ("SIMPLE".equalsIgnoreCase(strategyType)) { strategy = new SimpleInterestCalculator(); } else { System.err.println("Unknown/unsupported strategy-type"); } } return strategy; } }
在这种情况下,我们测试策略模式的代码如下所示:
package design.strategy; public class Main1 { public static void main(String[] args) { StrategyFactory factory = new StrategyFactory(); Account acct1 = new Account(12345678l, "Solo Cui", AccountType.SAVING); acct1.setInterestStrategy(factory.createStrategy("COMPOUND")); acct1.deposit(10000.0d); System.out.print(acct1); System.out.println(" has interest : " + acct1.getInterest(5)); Account acct2 = new Account(12345680l, "Killer X", AccountType.SAVING); acct2.setInterestStrategy(factory.createStrategy("SIMPLE")); acct2.deposit(10000.0d); System.out.print(acct2); System.out.println(" has interest : " + acct2.getInterest(5)); } }
以下是使用工厂模式的程序输出:
Account [accountNo=12345678, accountHolderName=Solo Cui, accountType=SAVING, rate=2.0, interestStrategy=Compound Interest, amount=10000.0] has interest : 1040.8080320000008
Account [accountNo=12345680, accountHolderName=Killer X, accountType=SAVING, rate=2.0, interestStrategy=Simple Interest, amount=10000.0] has interest : 1000.0
创建AccountType接口来定义具有预定义或固定利率的账户类型。和策略模式类似,代码如下:
package design.strategy; public enum AccountType { SAVING (2.0d), CURRENT (1.0d); private double rate; AccountType (final double rate) { this.rate = rate; } public double getRate() { return rate; } }
为该账户实现一个抽象的基类,并通过对其进行子类化来创建各种类型的Account。
以下是账户的代码。 请注意,这里已将类定义为抽象类,以强制对其进行子类化。
package design.strategy; public abstract class Accounta { private long accountNo; private String accountHolderName; private AccountType accountType; private String interestStrategy; private double amount; public Accounta() { super(); } public Accounta(long accountNo, String accountHolderName, AccountType accountType) { this(); this.accountNo = accountNo; this.accountHolderName = accountHolderName; this.accountType = accountType; } public long getAccountNo() { return accountNo; } public void setAccountNo(long accountNo) { this.accountNo = accountNo; } public String getAccountHolderName() { return accountHolderName; } public void setAccountHolderName(String accountHolderName) { this.accountHolderName = accountHolderName; } public AccountType getAccountType() { return accountType; } public void setAccountType(AccountType accountType) { this.accountType = accountType; } public String getInterestStrategy() { return interestStrategy; } public void setInterestStrategy(String interestStrategy) { this.interestStrategy = interestStrategy; } public double getAmount() { return amount; } public void setAmount(double amount) { this.amount = amount; } public void deposit(double amount) { // 检查数额的有效性 if (amount > 0.0d) { this.amount += amount; } } public void withdraw(double amount) { // c检查数额的有效性以及低于账户的可用金额 if (amount > 0.0d && amount < this.amount) { this.amount -= amount; } } // 此方法需要子类定义,并实现正确的其正确算法 public abstract double getInterest(int term); @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("Account [accountNo=").append(getAccountNo()).append(", accountHolderName=") .append(getAccountHolderName()).append(", accountType=").append(getAccountType()).append(", rate=") .append((getAccountType() != null) ? getAccountType().getRate() : 0.0d).append(", interestStrategy=") .append(getInterestStrategy()).append(", amount=").append(getAmount()).append("]"); return builder.toString(); } }
现在,必须为账户创建类型。创建SavingAccount,它与复合利息算法捆绑在一起。
package design.factory; public class SavingAccount extends Account { public SavingAccount(long accountNo, String accountHolderName) { super(accountNo, accountHolderName, AccountType.SAVING); setInterestStrategy("Compound Interest"); } @Override public double getInterest(int term) { if (this.getAccountType() != null) { return this.getAmount() * Math.pow(1.0 + this.getAccountType().getRate()/100.0, term) - this.getAmount(); } return 0.0d; } }
接下来,我创建了CurrentAccount,它与简单利息算法捆绑在一起。
package design.factory; public class CurrentAccount extends Account { public CurrentAccount(long accountNo, String accountHolderName) { super(accountNo, accountHolderName, AccountType.CURRENT); setInterestStrategy("Simple Interest"); } @Override public double getInterest(int term) { if (this.getAccountType() != null) { return ((this.getAmount() * term * this.getAccountType().getRate()) / 100); } return 0.0d; } }
基本上,账户类型不仅具有预定义的费率(如策略中),而且还具有紧密耦合的预定义利息计算算法。 因此,账户实例将具有已定义的功能。
现在,让我们来看看最重要的一步,即根据给定的账户类型为账户定义工厂类(AccountFactory)。
package design.factory; public class AccountFactory { public Account createAccount(long accountNo, String accountHolderName, String accountType) { Account account = null; AccountType type = AccountType.valueOf(accountType); if (type != null) { switch (type) { case SAVING: account = new SavingAccount(accountNo, accountHolderName); break; case CURRENT: account = new CurrentAccount(accountNo, accountHolderName); break; default: // 创建新账户类型失败情况 System.err.println("Unknown/unsupported account-type."); } } else { System.err.println("Undefined account-type."); } return account; } }
那么现在,让我们看看测试工厂模式的代码。
package design.factory; public class Main { public static void main(String[] args) { AccountFactory factory = new AccountFactory(); Account acct1 = factory.createAccount(12345678l, "Solo Cui", "SAVING"); acct1.deposit(10000.0d); System.out.print(acct1); System.out.println(" has interest : " + acct1.getInterest(5)); Account acct2 = factory.createAccount(12345680l, "Killer X", "CURRENT"); acct2.deposit(10000.0d); System.out.print(acct2); System.out.println(" has interest : " + acct2.getInterest(5)); } }
以下是使用工厂模式的程序输出:
Account [accountNo=12345678, accountHolderName=Solo Cui, accountType=SAVING, rate=2.0, interestStrategy=Compound Interest, amount=10000.0] has interest : 1040.8080320000008
Account [accountNo=12345680, accountHolderName=Killer X, accountType=CURRENT, rate=1.0, interestStrategy=Simple Interest, amount=10000.0] has interest : 500.0
好吧,就这些了!
我希望本教程能够帮助展示策略和工厂模式之间的区别——记住对照着模式关键点来思考两种模式下的示例,可以有助于你更好的理解。但具体使用那种模式,则需要,有时更多是取决于偏好或业务场景。
怎么样,喜欢这篇文章吧? 请不要忘记按下按钮点个赞,分享出去吧。 祝你编码快乐!