dump-2015: «Как сменить язык программирования и не...

101
В презентации содержится 33 слайда с кодом, сцены насилия, негуманного обращения с разработчиками и жестокого рефакторинга Беременным женщинам, детям, лицам страдающим паранойей и неспособностью воспринимать код , а также менеджерам от просмотра рекомендуется воздержаться

Upload: it-people

Post on 15-Jul-2015

356 views

Category:

Internet


3 download

TRANSCRIPT

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

Беременным женщинам, детям, лицам страдающим паранойей и неспособностью воспринимать код, а также менеджерам

от просмотра рекомендуется воздержаться

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

@mogilnikov

18 лет стажа

18 лет стажа

Некоторые ролики на YouTube начинаются с чего-то

похожего

Опыт

C# (Win)

Erlang (Web)

Ruby (Web)

Delphi (Win)

Опыт

C# (Win)

Erlang (Web)

Ruby (Web)

C++ (Win)

Как это началось

Герой

Босс

Босс

Пишем на C#!

Да ну?

Ну да!

А есть возражения?

Герой

Ладно

Задача

Написать простой биллинг: счета, деньги, валюты…

вот это всё

Впереди 20 слайдов с кодом подряд. Не забывайте дышать!

Диаграмма классов

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?!

Босс доволенАй, молодец!

Только пример так себе

Проблема босса

Программировать на статически типизированных языках?

В моём королевстве???!!!

Босс

Блин, надо было использовать dynamic

Новый работодатель

Новый работодатель

Пишем на Ruby!

Герой

Не надо всё переписывать! И так

работает же!

Новый работодатель

Новый работодатель

Герой

Ладно

Герой

Ведь Ruby - это как C#, только интерпретируемый и с

утиной типизацией!

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

Профсоюз Ruby-разработчиков потребовал вырезать сцены чрезмерного насилия над

языком

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

Итог

Герой

Ну, как?

CTO

Менеджер проекта

CEO

Ой

Их код не прошёл Code Review

Дорогие детишки, поможем программисту?

Или пускай повесит пока?

В чём была проблема?

В чём была проблема?

Механически перенёс код в другой синтаксис

Это новый взгляд на окружающий мир

Новый язык программирования

Другой менталитет

Как объекты реального мира представляются в коде?

Другой менталитет

Как производится декомпозиция?

Другой менталитет

Как повторно использовать код?

Другой менталитет

Как код обменивается данными с другим кодом?

Другой менталитет

Какие практики признаны порочными, а какие правильными?

Какой менталитет у 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

Акцент на скорость разработки

и высокую читаемость кода

Ruby пятью «словами»

Объектно-ориентированный

Динамическая типизация

Метапрограммирование

Культ тестов

Влияние функционального программирования

Объектно-ориентированный

Объект - значимая сущность предметной области, и ничего больше

Никаких «сервисов», «хелперов» и прочего

Динамическая типизация

Не только аргументы и переменные без типов, но и

другой взгляд на декомпозицию

Более гибкий код

Метапрограммирование

Код, генерируемый «на лету», в зависимости от данных

Повышает читаемость и удобство

Культ тестов

Можно «замокать» любой метод любого объекта

Пишется «читаемый», а не «тестируемый» код

Влияние ФП

Лямбды везде, повсюду

FP-фаги одобряют

Как писать на Ruby?

№1 Купи макбук

№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'

№3: скобки, убирай скобки!!111

lalala(op, pa) => lalala op, pa

Как должен выглядеть код?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

И напиши уже тесты!

Роскомнадзор потребовал удалить код, разжигающий ненависть по отношению к тестам, написанным не на

динамических языках

Так как учить новые языки?

№1 Разберитесь в менталитете

языка

Найдите статью

Так как учить новые языки?

№2 Выполните упражнения

Есть для множества языков

Так как учить новые языки?

№3 Посмотрите код какого-нибудь

проекта

Небольшого, берегите мозг

Так как учить новые языки?

№4 Портируйте свой код

Покажите его кому-нибудь

Так как учить новые языки?

№5 Пишите код

Так как учить новые языки?

№6 Пишите код

Так как учить новые языки?

№7 А вот сейчас можно прочитать

книжку

Систематизирует знания

Изучайте новые языки!

Изучайте новые языки!

For The Greater Good!

Где найти код

https://github.com/amogil/dump-2015-c-sharp

https://github.com/amogil/dump-2015-ruby

Благодарности. Ну, правда