Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Как сменить язык программирования и не притащить старые привычки в новый мир

Как сменить язык программирования и не притащить старые привычки в новый мир

Многие разработчики пробуют изучать новые языки. Не всегда это происходит успешно и разработчик, испытав невыносимые душевные муки в процессе, заканчивает подход вердиктом: «Ерунда этот ваш %language%!». В докладе на примере перехода с C# на Ruby я покажу, почему это происходит и что делать, чтобы погружение в новый язык было легким, приятным и продуктивным.

Alexey Mogilnikov

March 20, 2015
Tweet

Other Decks in Programming

Transcript

  1. В презентации содержится 33 слайда с кодом, сцены насилия, негуманного

    обращения с разработчиками и жестокого рефакторинга Беременным женщинам, детям, лицам страдающим паранойей и неспособностью воспринимать код, а также менеджерам от просмотра рекомендуется воздержаться
  2. 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; } }
  3. 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; } } … }
  4. 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); } }
  5. 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; } }
  6. 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 }
  7. 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); }
  8. 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); }
  9. 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(); } }
  10. 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(); } }
  11. 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); } }
  12. 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); } }
  13. 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; } }
  14. 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); }
  15. 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); } }
  16. 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); }
  17. ----------- RECEIPT ----------- Customer: House Stark (Winter is Coming) Balance:

    1500 Stags King's envy: 22 ------------------------------- ----------- RECEIPT ----------- Customer: House Lannister (Hear Me Roar!) Balance: 9999 Dragons King's envy: 1214 ------------------------------- ----------- RECEIPT ----------- Customer: House Stark (Winter is Coming) Balance: 690.0 Stags King's envy: 10 ------------------------------- ----------- RECEIPT ----------- Customer: House Lannister (Hear Me Roar!) Balance: 10099 Dragons King's envy: 1226 -------------------------------
  18. class Owner attr_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; } }
  19. class Account attr_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; } } … }
  20. 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
  21. 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) end end
  22. 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 Transaction attr_reader :id, :account, :amount, :reference, :comment def initialize(account, amount, comment) @id = SecureRandom.uuid @comment = comment @amount = amount @account = account end end
  23. 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 Money attr_reader :amount, :currency def initialize(amount, currency) @amount = amount @currency = currency end end
  24. public enum Currency { Dragons, Stags, Groats, Pennies } module

    Currency DRAGONS = 0 STAGS = 1 GROATS = 2 PENNIES = 3 def self.name_of(value) constants.find { |name| const_get(name) == value }.capitalize end end
  25. 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
  26. 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
  27. 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 Итог
  28. CTO

  29. CEO

  30. Какой менталитет у 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
  31. Динамическая типизация Не только аргументы и переменные без типов, но

    и другой взгляд на декомпозицию Более гибкий код
  32. №1: миксины module SharedCode def say_hello puts "Hello, my name

    is #{self.class.name}!" end end class Vasya include SharedCode end class Petya include SharedCode end class Dyatel end 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
  33. №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}'" end end end demo = Demo.new demo.find_by_name "Vasya" # => Search for field 'name' with value 'Vasya' demo.find_by_age 14 # => Search for field 'age' with value '14'
  34. Как должен выглядеть код? 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
  35. 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 Выкинь «вредные» объекты И скобки :)
  36. 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 end end
  37. 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 "" end end
  38. 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 end end
  39. Создавай объекты стильно! 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 end end class Float include Demo::MoneyMethods end class Fixnum include Demo::MoneyMethods end
  40. 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 end end
  41. И напиши уже тесты! Роскомнадзор потребовал удалить код, разжигающий ненависть

    по отношению к тестам, написанным не на динамических языках
  42. Так как учить новые языки? №7 А вот сейчас можно

    прочитать книжку Систематизирует знания