Обновить
25
0.1
ApeCoder@ApeCoder

Разработчик

Отправить сообщение
Я совершенно согласен с тем что название должно отражать назначение и с тем, что «циклы шифрования» не очень хорошее название.

Во-первых, если есть устойчивое сложившееся название это может окупить его неправильность (помнится, меня кто-то призывал использовать DDD :) )

Во-вторых, давайте разберем исходный пример по подробнее:

# 1)Разбить исходный текст на блоки по 64 бита
def modifiedDES(stream, key):
     #2)Для каждого такого блока
     blocks = split_to_blocks(stream)
     for block in blocks:
          encryptBlock(block, key) 
# {
def encryptBlock(block):
    #  2.1)Переставить биты в блоке местами по определённому алгоритму
    initialPermutation(block)
    #  2.2)Разбить блок на два блока длиной 32 бита (далее — левый и правый)
    (left, right) = splitBlockToHalves(block)
    # 2.3)Повторить 16 раз:
    subKeys = generateSubkeysFrom(key)
    for subKey in subKeys:
         #  {
         #    2.3.1)Вычислить Ki из ключа шифрования K и номера итерации по определённому алгоритму.     
         #    2.3.2)Высичлить блок E длиной 48 из правого блока длиной 32 по определенному алгоритму.
         expansion = expand(right)
         #    2.3.3)F = побитовая сумма Ki и E
         F = subKey xor E
         #   2.3.4)левый блок длиной 32 = правый блок длиной 32 бита на прошлой итерации
         #  2.3.5)правый блок длиной 32 = левый блок 32 на прошл итерации, побитово сложенный с F
         (left, right) = (right, left xor F)
      #  2.3.6)Добавить левый блок в конец результата шифрования
      result.write(left)
      #   2.3.7)Добавить правый блок в конец результата шифрования
      result.write(right)  


Итого

# итерация 1
def modifiedDES(stream, key):
     blocks = split_to_blocks(stream)
     for block in blocks:
          encryptBlock(block, key) 
def encryptBlock(block):
    initialPermutation(block)
    (left, right) = splitBlockToHalves(block)
    subKeys = generateSubkeysFrom(key)
    for subKey in subKeys:
         expansion = expand(right)
         F = subKey xor E
         (left, right) = (right, left xor F)
    result.write(left)
    result.write(right)  


Итого длина самого большого метода 9 строк, за счет выноса шифрования блока в отдельную функцию и лаконичных обозначений в стиле Pythion.

Следующий шаг «4.3 Основная функция шифрования (функция Фейстеля)»

Анализ — функция получает подключ и половину блока, выдает преобразованную половину блока. Мне кажется вполне
логично назвать ее шифрованием половины блока (encryptBlockHalf)

# итерация 2
def modifiedDES(stream, key):
     blocks = split_to_blocks(stream)
     for block in blocks:
          encryptBlock(block, key) 
def encryptBlock(block):
    initialPermutation(block)
    (left, right) = splitBlockToHalves(block)
    subKeys = generateSubkeysFrom(key)
    for subKey in subKeys:
         (left, right) = (right, left xor encryptBlockHalf(subkey, right))
    result.write(left)
    result.write(right)  
def encryptBlockHalf(subKey, blockHalf):
    # основная функция шифрования
    return subKey xor expand(blockHalf)


Самая длинная функция — 7 строк

Итерация 3 — Циклы шифрования
Циклы шифрования берут набор подключей и функцию шфврования полублока и последовательно ее применяют к полублокам пары по отношению к текущему ключу:

Вход: подключи и функция шифрования половинки;

# итерация 3
def modifiedDES(stream, key):
     blocks = splitToBlocks(stream)
     for block in blocks:
          encryptBlock(block, key) 

def encryptBlock(block):
    initialPermutation(block)
    subKeys = generateSubkeysFrom(key)
    encryptBlockByHalves(block, subkeys, encryptBlockHalf)

def encryptBlockByHalves(block, subkeys, encryptBlockHalf):
    (left, right) = splitBlockToHalves(block)
    for subKey in subKeys:
         (left, right) = (right, left xor encryptBlockHalf(subkey, right))
    result.write(left)
    result.write(right)  

def encryptBlockHalf(subKey, blockHalf):
    # основная функция шифрования
    return subKey xor expand(blockHalf)


Самая длинная функция — 5 строк.
Мы рассматриваем вот это:

Для меня красная тряпка когда добавляют комментарии в длинный метод разбивая его на части типа:


То есть метод разбит на логические куски коментариями

Используйте IterationIdx и TransformIdx, в чем проблема?


В том, что компилятором не контроллируется.
Мне кажется не надо сливать XMLНоды, должен быть слой, который транслирует понятия документов на XML.
Не видим, комментарий может быть сверху за границами экрана.

Не далее, чем заголовок метода при той же разбивке. Лично мне удобнее смотреть на код — где я, callstack для того откуда я пришел.


Далее — заголовок метода у меня в колстеке (такое окно в дебагерре) а комментарий вообще может быть не в этом окне.
(например я поставил точку останова в функции MessageBox и вижу колстек main \ ParseFile \ MessageBox а не main \ MessageBox — чтобы увидеть, что брейкпоинт сработал именно при разборе файла, мне надо открутиться вверх до main по колстеку, а потом открутиться вверх до // parse file в редакторе )

2. Ключевые слова — язык не контролирует то есть если вы и будете использовать уникальные переменные для блока легко ошибиться.

Приведите пример.



// parse file
....
int i = MaxParseIterations;
while (i>0)
{
}
...
// transform
...
while (i < 1000 )
{
}
...


Ну почему же? В вашем примере, если блок предварен чем-то вроде «парсим файл, находим клиента», и при этом есть переменные FileName и Customer, то понять несложно. Если еще использовать что-то вроде венгерской нотации (вроде), то еще проще: например, у нас в команде принято все поля данных в методе предварять F, все локальные переменные — l, формальные параметры — A. тут будет уже lCustomer и AFileName.


То, что вы описали для меня — за гранью.

В-общем, компилятор и тулзы они хорошие — если им рассказать побольше — они помогут, а коментариев они не понимают.

Я не тролль, я отвечал на пачку комментариев по порядку и не обновил, извините :)

Повторюсь в очередной раз, 3й пример — это немного модифицированный DES, а не сам DES.

Извините, не понял. где модификация DES — вы заменили функцию для шифрования полублоков на свою?
По-моему, рассматривать организацию процедур, как средство поименования блоков кода некорректно. Изначальный смысл еще с эпохи машинного кода совсем иной — переход к куску и возврат обратно.


Изначальный смысл безразличен, главное — как удобнее.

Если просто в callstack'е при отладке — не страшно, мы и так видим, где мы сейчас.


Не видим, комментарий может быть сверху за границами экрана.

Если при exception'е — есть инструменты, которые выдают номер строки (у нас используется Eurekalog для Delphi, в лог при обвале идет callstack с номерами строк в модулях).
В общем, зависит от используемых инструментов.


Осталось по номеру определить комментарий, которым был помечен блок и отличить его от комментария к конкретной строке.

— мы не сможем свернуть его в редакторе

Ни разу не приходилось сворачивать. Видимо, по-разному с кодом работаем.


Тогда если у вас есть N блоком прокоментированных сверху

// Разбор файла
// Преобразования
// Запись

Как увидеть общий план?
— язык не контролирует использование переменных (во-первых можно случайно использовать переменную из верхнего закомментированного блока, во-вторых, при чтении всегда придется учитывать эту структуру

Вполне можно использовать переменные с ограниченной областью видимости (в С++, например) или определять переменные для каждого блока свои и не использовать их повторно.


1. Я не очень хорошо знаю C++ он позволяет писать так?

// разбор файла 
{
   handle = readFile(name)
   .... куча кодв
   close (handle)  
}


2. Ключевые слова — язык не контролирует то есть если вы и будете использовать уникальные переменные для блока легко ошибиться.
Насчет закомментированного — предпочитаю следовать правилу — никаких закомментированных блоков в репозитории. Комментарии — для комментариев, для остального — система контроля версий.


Я имел ввиду комментированный блок — блок, предваряемый комментарием.

— нет возможности декларировать исходные данные и результаты для каждого куска

Очень общее утверждение, сложно что-то обсуждать.


Customer parseFile(string fileName)
{
... куча кода
}


легко понять что метод требует в качестве исходных данных, что возвращает и это контроллируется компилятором

// parsing file
... куча кода


трудно понять что код берет в качестве исходных данных, что возвращает, а что явлется его внутренними делами и вообще говоря непонятно, к чему относится комментарий, к первой строке, к 10 строкам или к чему

«вы зачем-то переносите из википедии код не 1-в-1 а инлайня функции»

Это было сделано намеренно.


Ок мы не понимаем алгоритм но при этом отказываемся использовать обозначений от тех, кто понимает.

Первая цель — показать, как авторы самого алгоритма занимались декомпозицией. Вторая цель — так как мы модифицируем алгоритм, содержимое «функции Фейстеля» изменяется, и это больше не «функция Фейстеля» (т.к. она делает другие операции).


Мы можем ее переименовать на «зашифроватьПолублок»

Соответственно, мы не можем использовать ту же самую декомпозицию и туже самую абстракцию, что использовали авторы DES, и толк от неё теряется.


Толк простой — для сопоставления с исходным опиванием не надо искать какие-то паттерны в коде, а надо просто сопоставить названия частей.

«Циклы шифрования»
Что выражает название метода «Циклы шифрования»? Что это что-то, как то связанное с шифрованием и что внутри есть цикл. То что это связано с шифрованием, итак ясно из контекста использования метода. То что в названии функции отражено что внутри есть цикл — это вообще нарушение инкапсуляции. Это как раз пример плохой синтетической абстракции, о которой я говорил ранее.


1. Название «Лопата» никак не отражает его предназначения, но проще использовать его, чем каждый раз говорить «Штык и древко для копания» — со временем слово лопата запомнится и статен привычной.Мы можем вводить понятия и давать им неговорящие названия, это тоже имеет ценность больше, чем не давание названий. например со словом «кварк» проще чем его не иметь и каждый раз расшифровывать это понятие.
2. Давайте декомпозируем эти циклы я бы представил это так:
let encryptionStep encryptBlockHalf (Left,Right) key = Right, (Left xor (encryptBlockHalf key)) 
let encryptionLoops block encryptBlockHalf keys = keys |> fold (encryptionStep encryptBlockHalf) (splitBlock block)  


То есть она берет блок и коллекцию ключей и делает свертку полоинок по функции шифрования полублока.
То есть перед нами способ задействовать N ключей полублоков для шифрования блока.

можно ее назвать, например, encryptBlockByHalves.

Это всё равно что дать методу имя «Сделать хорошо», а классу — «Manager» или «Service». Эти названия ничего не отражают.


Тут вопрос, что лучше, если не знаешь как придумать название — не давать никакого и не делать никакой структуры или дать какую-то структуру. Мне кажется лучше дать какую-то структуру — даже если имя с первого раза ничего не будет говорить, при последовательном обращении обозначения запомнятся и проще будет находить соответствие. Например, я сейчас сопоставляю английскую и русскую версию статьи про DES и вижу, что:
1. С русской работать лучше так как структура вынесена в оглавление.
2. Название функции Фейстеля повзоляет легко сопоставить части алгоритма.
То что для одного непонятно, для другого — очевидно


Вероятно любое описание предназначается для того кому, не все до конца очевидно. Программирование выработало некоторые требования к формальным описаниям для повышения понятности, например стоит использовать константы вместо магических чисел.

Вот вы понимаете, почему ядро атома не разлетается на кусочки в то время как внутри него действуют чудовищные отталкивающие силы? А если вас попросят запрограммировать модель атома, будет ли для вас проблемой то что вы это не понимаете, точно также, как для вас проблема, что вы не понимаете почему в DES-е 16 итераций?


Вполне возможно, что будет, так как если нет притягивающих сил, то моя модель атому будет разлетаться в отличие от реальных атомов (и очень трудно будет выявлять ошибки не понимая почему она должна не разлетаться).

Самый простой способ эти абстракции придумать — разбить алгоритм на «Функция имени меня», «Функция имени вон той планеты», «Функция имени моего домашнего животного»,


4.3 Основная функция шифрования (функция Фейстеля)

Почему вы решили прочесть только то, что в скобках?
Почему авторы статьи википедии считают, что понятнее разбить алгоритм на этапы, а вы, не понимая самого алгоритма решили что лучше написать одной большой простыней не давая этапам никакого названия.

Фактически, я бы написал алгоритм так (псевдо F#)

let DES x = x |> splitToBlocks |> encryptBlock |> joinBlocks
let encryptBlock x = x |> initialPermutation |> encryptLoops mainEncryption |> finalPermutation


И если уж на то пошло, понятней все таки назвать кусок алгоритма «функцией Фейстеля» представьте себе бедолагу, который попытается этот код отладить и эта функция не будет явно присутствовать — чтобы понять что есть что ему придется просматривать глазами весь алгоритм, вместо того, что бы просто сопоставить этот кусок кода с википедией.

И если раздробите вы плохо декомпозирующийся алгоритм на части, понятнее он от этого не станет.


Авторы статьи в википедии это уже за вас сделали — зачем вы уничтожили эту структуру в своем псевдокоде?
Почему вы называете плохо декомпозирующимся алгоритм, хотя сами признаете что его не понимаете?

Примеры я уже приводил.


Приведено три примера:
1) Разбор сайта — не является длинным методом (всего пять строчек) — длинный метод не приведен. Приведите пожалуйста более длинный код.
2) Хаотический аборигенский танец — не понятен контекст преобразования танца в метод, скорее всего является фантазией автора а не реальной задачей (подтвердите, пожалуйста, или дайте больше контекста).
3) DES — в википедии уже разбит на стадии (наверное авторам так было понятней? ;)) почему-то решили что лучше знаете как описывать DES и слепили это в один комок.

Я пропустил что-то в этом списке?

С моей точки зрения трудность с нахождением реального примера связана с объективными характеристиками человеческого сознания. Наверняка есть такие примеры, но как правило они связаны с какой-то неидеальностью: недостаточной оптимизации компилятором (развернутые циклы), нашим непониманием предметно области или недостаточной проработки экспертами.

а представляете, сколько уязвимостей в Windows молча работает годами и никто никогда их не найдет,


Какой ущерб от уязимости которую никто никогда не найдет?
Вообще, интересно было бы прочитать про какую-то методику сравнения ошибок наверное, надо как-то учитывать вероятность хака оцениваемую не только по количеству ошибок, но и по их серьезности и оперативности исправления?
Еще одна проблема вы зачем-то переносите из википедии код не 1-в-1 а инлайня функции. В википедии написано как раз лаконично

Схема шифрования алгоритма DES
4.1 Начальная перестановка
4.2 Циклы шифрования
4.3 Основная функция шифрования (функция Фейстеля)
4.4 Генерирование ключей
4.5 Конечная перестановка


Вы, не понимая назначения шагов, зачем-то решили поменять их названия или вообще опустить (заметим, что «функция имени кого-то» написана в скобках после «Основная функция шифрования»)

Если же вам придётся переводить сложную абстракцию постановщика user-story в непонятно для чего изобретённую вашу сложную абстракцию, то ваш мозг быстро закипит. В результате вы напишите гораздо больше кода с гораздо большим количеством ошибок. Советую вам почитать книгу о DDD Эрика Эванса.


Если бы авторы метода использовали константы с понятными названиями и выражали зависимости между ними ясно, это было бы понятней.
То есть здесь явно есть две проблемы:
1) Настоящие авторы метода написали непонятно
2) Вы не понимаете как он сделан и почему (именно потому, что 1) )

То есть в данном случае «длинный метод = проблема» срабатывает.
Не могли бы вы напротив каждой строчки добавить зачем она делается. А то получается, что автор метода не вы, а кто-то, написавший его на бумажке с непонятными обозначениями, а вы просто не понимая переписали на другом языке.

Например, от это 16 раз почему — 16, а не 32 или 7.

Т.е. в данном случае длина метода сигнализирует о кривизне в изначальной формулировке — по этому описанию невозможно понять, зачем сделан каждый шаг и почему так а не по другому…
Можете привести пример. Только реальный?
Дефекацию надо проводить в состоянии клинической смерти, после чего навсегда уходить из этого места?
В чем отличие гадюшника от серпентария? Разные виды змей, не только гадюки?
Мне кажется мои места работы являлись контрпримером к этому правилу
В языке есть средство для лечения имени куску кода. Если мы его не используем:
— мы не увидим в стектрейсе этого названия
— мы не сможем свернуть его в редакторе
— язык не контролирует использование переменных (во-первых можно случайно использовать переменную из верхнего закомментированного блока, во-вторых, при чтении всегда придется учитывать эту структуру
— нет возможности декларировать исходные данные и результаты для каждого куска

Для меня прокрутка менее удобна чем охватывание одним взглядом одним взглядом :)
Ps. Если методы нормально названы
А когда функция находится в боевом применении и работает, то причины на её изменение для следования каким-то стандартам должны быть очень вескими


Если у нас есть хорошие тесты или инструмент, который разобьет метод гарантированно корректно, то не такими уж и вескими

Информация

В рейтинге
4 744-й
Откуда
Россия
Дата рождения
Зарегистрирован
Активность