dump-2015: «Как сменить язык программирования и не...
TRANSCRIPT
В презентации содержится 33 слайда с кодом, сцены насилия, негуманного обращения с разработчиками и жестокого рефакторинга
Беременным женщинам, детям, лицам страдающим паранойей и неспособностью воспринимать код, а также менеджерам
от просмотра рекомендуется воздержаться
public class Owner : IOwner{public Owner(string title, string castle, string words, Money initBalance){Title = title;Castle = castle;Words = words;Account = new Account(initBalance.Currency, initBalance.Amount);
}
public IAccount Account { get; set; }public string Title { get; private set; }public string Words { get; private set; }public string Castle { get; private set; }
}
public class Account : IAccount{
public Account(Currency currency, decimal initialBalance){Id = Guid.NewGuid().ToString();Currency = currency;InitialBalance = initialBalance;_transactions = new List<ITransaction>();
}
public string Id { get; private set; }public Currency Currency { get; private set; }public decimal InitialBalance { get; private set; }private readonly List<ITransaction> _transactions;
public IEnumerable<ITransaction> Transactions{get { return _transactions; }
}…
}
public class Account : IAccount{…public void AddTransaction(ITransaction transaction){if (_transactions.Any(t => t.Id == transaction.Id))throw new ApplicationException("Hodor?");
if (transaction.Account != this)throw new ApplicationException("Hodor!");
_transactions.Add(transaction);}
public Money GetBalance(){var amount = InitialBalance +
_transactions.Sum(transaction => transaction.Amount);return new Money(amount, Currency);
}}
public class Transaction : ITransaction{public Transaction(IAccount account, decimal amount, string comment){Id = Guid.NewGuid().ToString();Comment = comment;Amount = amount;Account = account;
}
public string Id { get; private set; }public IAccount Account { get; private set; }public decimal Amount { get; private set; }public ITransaction Reference { get; private set; }public string Comment { get; private set; }
}
public class Money{public Money(decimal amount, Currency currency){Amount = amount;Currency = currency;
}
public decimal Amount { get; private set; }public Currency Currency { get; private set; }
}
public enum Currency{Dragons,Stags,Groats,Pennies
}
public void Show(){var starksInitMoney = new Money(1500, Currency.Stags);var lannistersInitMoney = new Money(9999, Currency.Dragons);var starks = new Owner("House Stark", "Winterfell", "Winter is Coming",
starksInitMoney);var lannisters = new Owner("House Lannister", "Casterly Rock", "Hear Me Roar!",
lannistersInitMoney);
_receiptConsolePrinter.Print(starks);_receiptConsolePrinter.Print(lannisters);
var money = new Money(100, Currency.Dragons);_transferService.Transfer(starks.Account, lannisters.Account, money,
"Pwned by Lannisters");
_receiptConsolePrinter.Print(starks);_receiptConsolePrinter.Print(lannisters);
}
public void Show(){var starksInitMoney = new Money(1500, Currency.Stags);var lannistersInitMoney = new Money(9999, Currency.Dragons);var starks = new Owner("House Stark", "Winterfell", "Winter is Coming",
starksInitMoney);var lannisters = new Owner("House Lannister", "Casterly Rock", "Hear Me Roar!",
lannistersInitMoney);
_receiptConsolePrinter.Print(starks);_receiptConsolePrinter.Print(lannisters);
var money = new Money(100, Currency.Dragons);_transferService.Transfer(starks.Account, lannisters.Account, money,
"Pwned by Lannisters");
_receiptConsolePrinter.Print(starks);_receiptConsolePrinter.Print(lannisters);
}
public class ReceiptConsolePrinter : IReceiptConsolePrinter{private readonly IKingsMentalStateCalculator _kingsMentalStateCalculator;
public ReceiptConsolePrinter(IKingsMentalStateCalculator kingsMentalStateCalculator){_kingsMentalStateCalculator = kingsMentalStateCalculator;
}
public void Print(IOwner owner){var balance = owner.Account.GetBalance();Console.WriteLine("----------- RECEIPT -----------");Console.WriteLine("Customer: {0} ({1})", owner.Title, owner.Words);Console.WriteLine("Balance: {0} {1}", balance.Amount, balance.Currency);Console.WriteLine("King's envy: {0}", _kingsMentalStateCalculator.GetEnvy(owner));Console.WriteLine("-------------------------------");Console.WriteLine();
}}
public class ReceiptConsolePrinter : IReceiptConsolePrinter{private readonly IKingsMentalStateCalculator _kingsMentalStateCalculator;
public ReceiptConsolePrinter(IKingsMentalStateCalculator kingsMentalStateCalculator){_kingsMentalStateCalculator = kingsMentalStateCalculator;
}
public void Print(IOwner owner){var balance = owner.Account.GetBalance();Console.WriteLine("----------- RECEIPT -----------");Console.WriteLine("Customer: {0} ({1})", owner.Title, owner.Words);Console.WriteLine("Balance: {0} {1}", balance.Amount, balance.Currency);Console.WriteLine("King's envy: {0}", _kingsMentalStateCalculator.GetEnvy(owner));Console.WriteLine("-------------------------------");Console.WriteLine();
}}
public class KingsMentalStateCalculator : IKingsMentalStateCalculator{private const decimal KingsEnvyThresholdGroats = 100;private const decimal KingsGrace = 1000;private readonly ICurrencyConverter _currencyConverter;
public KingsMentalStateCalculator(ICurrencyConverter currencyConverter){_currencyConverter = currencyConverter;
}
public int GetEnvy(IOwner owner){var balance = owner.Account.GetBalance();var balanceInGroats = _currencyConverter.Convert(balance.Currency, Currency.Groats,
balance.Amount);var incomesWithoutTrash = owner.Account.Transactions.Select(t => _currencyConverter.Convert(owner.Account.Currency,Currency.Groats, t.Amount))
.Where(amount => amount > KingsEnvyThresholdGroats);var transactionsCountToEnvy = incomesWithoutTrash.Count();if (transactionsCountToEnvy == 0) transactionsCountToEnvy = 1;var envy = balanceInGroats/(transactionsCountToEnvy*KingsGrace);return Convert.ToInt32(envy);
}}
public class KingsMentalStateCalculator : IKingsMentalStateCalculator{private const decimal KingsEnvyThresholdGroats = 100;private const decimal KingsGrace = 1000;private readonly ICurrencyConverter _currencyConverter;
public KingsMentalStateCalculator(ICurrencyConverter currencyConverter){_currencyConverter = currencyConverter;
}
public int GetEnvy(IOwner owner){var balance = owner.Account.GetBalance();var balanceInGroats = _currencyConverter.Convert(balance.Currency, Currency.Groats,
balance.Amount);var incomesWithoutTrash = owner.Account.Transactions.Select(t => _currencyConverter.Convert(owner.Account.Currency,Currency.Groats, t.Amount))
.Where(amount => amount > KingsEnvyThresholdGroats);var transactionsCountToEnvy = incomesWithoutTrash.Count();if (transactionsCountToEnvy == 0) transactionsCountToEnvy = 1;var envy = balanceInGroats/(transactionsCountToEnvy*KingsGrace);return Convert.ToInt32(envy);
}}
public class CurrencyConverter : ICurrencyConverter{private static readonly Dictionary<Currency, decimal> DragonExchangeRates =
new Dictionary<Currency, decimal>{{Currency.Dragons, 1m},{Currency.Stags, 8.1m},{Currency.Groats, 121.4m},{Currency.Pennies, 268m},
};
public decimal Convert(Currency from, Currency to, decimal amount){var fromRate = DragonExchangeRates[from];var toRate = DragonExchangeRates[to];
return toRate*amount/fromRate;}
}
public void Show(){var starksInitMoney = new Money(1500, Currency.Stags);var lannistersInitMoney = new Money(9999, Currency.Dragons);var starks = new Owner("House Stark", "Winterfell", "Winter is Coming",
starksInitMoney);var lannisters = new Owner("House Lannister", "Casterly Rock", "Hear Me Roar!",
lannistersInitMoney);
_receiptConsolePrinter.Print(starks);_receiptConsolePrinter.Print(lannisters);
var money = new Money(100, Currency.Dragons);_transferService.Transfer(starks.Account, lannisters.Account, money,
"Pwned by Lannisters");
_receiptConsolePrinter.Print(starks);_receiptConsolePrinter.Print(lannisters);
}
public class TransferService : ITransferService{private readonly ICurrencyConverter _currencyConverter;
public TransferService(ICurrencyConverter currencyConverter){_currencyConverter = currencyConverter;
}
public void Transfer(IAccount from, IAccount to, Money money, string comment){var minusAmount = _currencyConverter.Convert(money.Currency, from.Currency,
money.Amount);var plusAmount = _currencyConverter.Convert(money.Currency, to.Currency,
money.Amount);var t1 = new Transaction(from, -minusAmount, comment);var t2 = new Transaction(to, plusAmount, comment);from.AddTransaction(t1);to.AddTransaction(t2);
}}
public void Show(){var starksInitMoney = new Money(1500, Currency.Stags);var lannistersInitMoney = new Money(9999, Currency.Dragons);var starks = new Owner("House Stark", "Winterfell", "Winter is Coming",
starksInitMoney);var lannisters = new Owner("House Lannister", "Casterly Rock", "Hear Me Roar!",
lannistersInitMoney);
_receiptConsolePrinter.Print(starks);_receiptConsolePrinter.Print(lannisters);
var money = new Money(100, Currency.Dragons);_transferService.Transfer(starks.Account, lannisters.Account, money,
"Pwned by Lannisters");
_receiptConsolePrinter.Print(starks);_receiptConsolePrinter.Print(lannisters);
}
----------- RECEIPT -----------Customer: House Stark (Winter is Coming)Balance: 1500 StagsKing's envy: 22-------------------------------
----------- RECEIPT -----------Customer: House Lannister (Hear Me Roar!)Balance: 9999 DragonsKing's envy: 1214-------------------------------
----------- RECEIPT -----------Customer: House Stark (Winter is Coming)Balance: 690.0 StagsKing's envy: 10-------------------------------
----------- RECEIPT -----------Customer: House Lannister (Hear Me Roar!)Balance: 10099 DragonsKing's envy: 1226-------------------------------
Всё просто!
Owner!
Currency!
Money!
ReceiptConsolePrinter
Account!
CurrencyConverter??!!
Transaction!
TransferService?
KingsMentalStateCalculator?!
class Ownerattr_reader :title, :castle, :words, :account
def initialize(title, castle, words, init_balance) @title = title @castle = castle @words = words @account = Account.new(init_balance.currency, init_balance.amount)end
end
public class Owner : IOwner{public Owner(string title, string castle, string words, Money initBalance){Title = title;Castle = castle;Words = words;Account = new Account(initBalance.Currency, initBalance.Amount);
}
public IAccount Account { get; set; }public string Title { get; private set; }public string Words { get; private set; }public string Castle { get; private set; }
}
class Accountattr_reader :id, :currency, :initial_balance, :transactions
def initialize(currency, initial_balance) @id = SecureRandom.uuid @currency = currency @initial_balance = initial_balance @transactions = []end…
end
public class Account : IAccount{
public Account(Currency currency, decimal initialBalance){
Id = Guid.NewGuid().ToString();Currency = currency;InitialBalance = initialBalance;_transactions = new List<ITransaction>();
}
public string Id { get; private set; }public Currency Currency { get; private set; }public decimal InitialBalance { get; private set; }private readonly List<ITransaction> _transactions;
public IEnumerable<ITransaction> Transactions{
get { return _transactions; }}…
}
public class Account : IAccount{
…public void AddTransaction(ITransaction transaction){
if (_transactions.Any(t => t.Id == transaction.Id))throw new ApplicationException("Hodor?");
if (transaction.Account != this)throw new ApplicationException("Hodor!");
_transactions.Add(transaction);}…
}
class Account… def add_transaction(transaction) if @transactions.any? { |t| t.id == transaction.id } raise StandardError.new("Hodor?") end if transaction.account != self raise StandardError.new("Hodor!") end @transactions.push(transaction)end…
end
public class Account : IAccount{
…public Money GetBalance(){var amount = InitialBalance + _transactions.Sum(transaction => transaction.Amount);return new Money(amount, Currency);
}}
class Account…
def get_balance amount = initial_balance + transactions.sum { |t| t.amount } return Money.new(amount, currency) endend
public class Transaction : ITransaction{public Transaction(IAccount account, decimal amount, string comment){Id = Guid.NewGuid().ToString();Comment = comment;Amount = amount;Account = account;
}
public string Id { get; private set; }public IAccount Account { get; private set; }public decimal Amount { get; private set; }public ITransaction Reference { get; private set; }public string Comment { get; private set; }
}
class Transactionattr_reader :id, :account, :amount, :reference, :comment
def initialize(account, amount, comment) @id = SecureRandom.uuid @comment = comment @amount = amount @account = accountend
end
public class Money{public Money(decimal amount, Currency currency){Amount = amount;Currency = currency;
}
public decimal Amount { get; private set; }public Currency Currency { get; private set; }
}
class Moneyattr_reader :amount, :currency
def initialize(amount, currency) @amount = amount @currency = currencyend
end
public enum Currency{Dragons,Stags,Groats,Pennies
}
module CurrencyDRAGONS = 0STAGS = 1GROATS = 2PENNIES = 3
def self.name_of(value) constants.find { |name| const_get(name) == value }.capitalizeend
end
public void Show(){var starksInitMoney = new Money(1500, Currency.Stags);var lannistersInitMoney = new Money(9999, Currency.Dragons);var starks = new Owner("House Stark", "Winterfell", "Winter is Coming",
starksInitMoney);var lannisters = new Owner("House Lannister", "Casterly Rock", "Hear Me Roar!",
lannistersInitMoney); …}
def show starks_init_money = Money.new(1500, Currency::STAGS) lannisters_init_money = Money.new(9999, Currency::DRAGONS) starks = Owner.new("House Stark", "Winterfell", "Winter is Coming", starks_init_money) lannisters = Owner.new("House Lannister", "Casterly Rock",
"Hear Me Roar!", lannisters_init_money) … end
public void Show(){…_receiptConsolePrinter.Print(starks);_receiptConsolePrinter.Print(lannisters);
var money = new Money(100, Currency.Dragons);_transferService.Transfer(starks.Account, lannisters.Account, money,
"Pwned by Lannisters");
_receiptConsolePrinter.Print(starks);_receiptConsolePrinter.Print(lannisters);
}
def show … @receipt_console_printer.print(starks) @receipt_console_printer.print(lannisters)
money = Money.new(100, Currency::DRAGONS) @transfer_service.transfer(starks.account, lannisters.account, money, "Pwned by Lannisters")
@receipt_console_printer.print(starks) @receipt_console_printer.print(lannisters)end
def show starks_init_money = Money.new(1500, Currency::STAGS) lannisters_init_money = Money.new(9999, Currency::DRAGONS) starks = Owner.new("House Stark", "Winterfell", "Winter is Coming", starks_init_money) lannisters = Owner.new("House Lannister", "Casterly Rock", "Hear Me Roar!», lannisters_init_money)
@receipt_console_printer.print(starks) @receipt_console_printer.print(lannisters)
money = Money.new(100, Currency::DRAGONS) @transfer_service.transfer(starks.account, lannisters.account, money, "Pwned by Lannisters")
@receipt_console_printer.print(starks) @receipt_console_printer.print(lannisters)end
Итог
Какой менталитет у Ruby
I hope to see Ruby help every programmer in the world to be productive, and to enjoy programming, and to be happy.
That is the primary purpose of Ruby language.
Matz
Ruby пятью «словами»
Объектно-ориентированный
Динамическая типизация
Метапрограммирование
Культ тестов
Влияние функционального программирования
Объектно-ориентированный
Объект - значимая сущность предметной области, и ничего больше
Никаких «сервисов», «хелперов» и прочего
Динамическая типизация
Не только аргументы и переменные без типов, но и
другой взгляд на декомпозицию
Более гибкий код
Метапрограммирование
Код, генерируемый «на лету», в зависимости от данных
Повышает читаемость и удобство
№1: миксиныmodule SharedCodedef say_helloputs "Hello, my name is #{self.class.name}!"
endend
class Vasyainclude SharedCode
end
class Petyainclude SharedCode
end
class Dyatelend
Vasya.new.say_hello # => "Hello, my name is Vasya!"Petya.new.say_hello # => "Hello, my name is Petya!"Dyatel.new.say_hello # => NoMethodError: undefined method
№2: метапрограммирование
class Demo# Read fields list from DB[:name, :phone, :age].each do |field|define_method "find_by_#{field}" do |value|puts "Search for field '#{field}' with value '#{value}'"
endend
end
demo = Demo.newdemo.find_by_name "Vasya" # => Search for field 'name' with value 'Vasya'demo.find_by_age 14 # => Search for field 'age' with value '14'
Как должен выглядеть код?def show starks_init_money = Money.new(1500, Currency::STAGS) lannisters_init_money = Money.new(9999, Currency::DRAGONS) starks = Owner.new("House Stark", "Winterfell", "Winter is Coming", starks_init_money) lannisters = Owner.new("House Lannister", "Casterly Rock", "Hear Me Roar!», lannisters_init_money)
@receipt_console_printer.print(starks) @receipt_console_printer.print(lannisters)
money = Money.new(100, Currency::DRAGONS) @transfer_service.transfer(starks.account, lannisters.account, money, "Pwned by Lannisters")
@receipt_console_printer.print(starks) @receipt_console_printer.print(lannisters)end
def show starks_init_money = Money.new 1500, Currency::STAGS lannisters_init_money = Money.new 9999, Currency::DRAGONS starks = Owner.new "House Stark", "Winterfell", "Winter is Coming", starks_init_money lannisters = Owner.new "House Lannister", "Casterly Rock", "Hear Me Roar!», lannisters_init_money
@receipt_console_printer.print starks @receipt_console_printer.print lannisters
money = Money.new 100, Currency::DRAGONS @transfer_service.transfer starks.account, lannisters.account, money, "Pwned by Lannisters"
@receipt_console_printer.print starks @receipt_console_printer.print lannistersend
Выкинь «вредные» объекты
И скобки :)
module TransferService def transfer(source_account, target_account, money, comment) minus = money.to(source_account.currency) plus = money.to(target_account.currency) t1 = Transaction.new source_account, -minus.amount, comment t2 = Transaction.new target_account, plus.amount, comment source_account.add_transaction t1 target_account.add_transaction t2 endend
module ReceiptConsolePrinter include KingsMentalStateCalculator
def print_receipt_for(owner) balance = owner.account.balance puts "----------- RECEIPT -----------" puts "Customer: #{owner.title} (#{owner.words})" puts "Balance: #{balance}" puts "King's envy: #{kings_envy_for owner}" puts "-------------------------------" puts "" endend
class Demo include ReceiptConsolePrinter include TransferService
def show starks_init_money = Money.new 1500, Currency::STAGS lannisters_init_money = Money.new 9999, Currency::DRAGONS starks = Owner.new "House Stark", "Winterfell", "Winter is Coming",
starks_init_money lannisters = Owner.new "House Lannister", "Casterly Rock", "Hear Me Roar!»,
lannisters_init_money
print_receipt_for starks print_receipt_for lannisters
money = Money.new 100, Currency::DRAGONS transfer starks.account, lannisters.account, money, "Pwned by Lannisters"
print_receipt_for starks print_receipt_for lannisters endend
Создавай объекты стильно!
module Demo module MoneyMethods CurrencyConverter::RATES.keys.each do |currency_name| define_method currency_name.to_s do Money.new self, currency_name end end endend
class Float include Demo::MoneyMethodsend
class Fixnum include Demo::MoneyMethodsend
class Demo include ReceiptConsolePrinter include TransferService
def show starks = Owner.new "House Stark", "Winterfell", "Winter is Coming", 1500.stags lannisters = Owner.new "House Lannister", "Casterly Rock", "Hear Me Roar!", 9999.dragons
print_receipt_for starks print_receipt_for lannisters
transfer starks.account, lannisters.account, 100.dragons, "Pwned by Lannisters"
print_receipt_for starks print_receipt_for lannisters endend
И напиши уже тесты!
Роскомнадзор потребовал удалить код, разжигающий ненависть по отношению к тестам, написанным не на
динамических языках
Где найти код
https://github.com/amogil/dump-2015-c-sharp
https://github.com/amogil/dump-2015-ruby