singalen: (Default)
Коллеги,

У меня к вам несколько вопросов. Я тут хочу приготовить тренинг, и нагло воспользоваться вашими идеями.

1. Какие приёмы и инструменты вы используете при работе с оным?
Отдельно - приёмы исследования того, что же, собственно, оно делает. Если можно, более-менее конкретно.
2. Как вы определяете оный код? По дублированию, непонятным именам, другим фаулеровским запахам, интуитивно?
3. Кто может поделиться хорошими, хорошо замудрёнными примерами легаси-кода? С зависимостями, багами, непонятными названиями и те пе.
Или из опенсорсных продуктов, или, если можно, проприетарных. Умеренно обфусцированный подойдёт.
Интересуют, наверное, куски размером от одного до пары десятков классов - чтобы можно было разобраться за несколько часов. Публиковать никуда не буду, нарисую аналогичные свои :D

И сам поделюсь книжкой: "Working Effectively with Legacy Code", Michael C. Feathers.
Книжка старая, вышла в 2004, до распространения TDD и рефакторингов. Слово mock там уже есть, и паттерн expect {} есть, а play {} ещё нет %) Ну и никаких библиотек типа Rhino ещё не упоминается.

В основном состоит из рецептов - какие рефакторинги применять в каком случае, c особым акцентом на том, как разбивать зависимости. И жёсткого требования на всё, что меняете, писать тесты.
Формализована не очень чётко, но почитать ради вдохновления и ради примеров стоит.

Лично я ожидал от книги ещё и инструментов исследования и отладки. Для меня легаси - в первую очередь то, что надо исследовать: понять, что и как оно делает, а у же потом чего-то менять.

Мои любимые инстументы - полнотекстовый поиск, дерево вызовов (там, где оно доступно), и "интрузивное исследование" (термин мой) - особым образом поломать существующий код и посмотреть, как он себя поведёт. Тут я ещё только собираюсь классифицировать свои собственные паттерны :D
Ну и юнит тесты как инструмент не/интрузивного исследования - тоже рулят, потому что очень сильно сокращают цикл "написал-запустил-проверил" - но это вы и сами знаете.
singalen: (sun)
Коллега в кодревю написал офигенную вещь, о коей мы (я) постоянно забываем. И зря, идея-то известная.
Делая пересмотр — коду ли, любой другой работе, даже человеку, — кроме критики, если видишь что-то хорошее (хорошую идею, скажем) — обязательно об этом напиши. Ревю пойдёт на порядок легче.
singalen: (Default)
//TODO: Do something else

код вокруг комментария, конечно, самодокументирующийся :-D
Tags:
singalen: (Default)
Коллеги, что вы думаете об "одноразовых" константах уровня файла?
Т.е. тем, которые вводятся и используются единожды, только чтобы соблюсти правило, и не локализуются.
Например, в SQL-выражениях или типа того:

private static final String NAME = "name";
...
rs.getString(NAME);


Лично я в свом коде их инлайню. Но если соглашение явно требует не держать констант в коде (а так оно и бывает, для простоты правил), то следую - почему бы и нет.
А вы?
singalen: (Default)

Originally published at Fiberglass flowers. You can comment here or there.

Till now, I did understand but didn’t remember the very situation, as I wasn’t the one creating thread pools and alike: all the core things were already there.
It’s a blessing to work for start-up.
You can think about

Monitor.Wait(toRetrieve); // will throw SynchronizationLockException

Now, after a look into docs, I see that one needs to hold the monitored object lock to avoid race conditions: a thread wakes up already holding a lock for an object it needs. The thread that did Pulse() acquired that lock and safely passed it to Wait()ing one.
Just watch out for deadlocks: it’s easy to wake up with one object lock, try to acquire another and die then…

lock(toRetrieve)
{
  Monitor.Wait(toRetrieve); // will throw SynchronizationLockException
}

As Doug Lea recommends in his excellent eternal “Concurrent Programming in Java“, just make it impossible. Lock only on a single object. If you really really need two locks, make them ordered: always acquire A then B.

If you do:

lock(a){
  // :
  lock (b)
  {
    //:
  }
  // or:
  b.SomethingThatLocks();
}

//then will you never ever do

lock(b)
{
  //:
  a.SomethingThatMightLock();
}

as calling other objects’ methods while holding a lock badly affects your karma.

For more in-detail look, read “EventWaitHandle and Monitors: how to pick one?” by Joe Duffy. He goes very deep down to system details.
He also did a nice brief overview of missed pulse problem - why do you always check wait condition in a loop:

while (toRetrieve.Count == 0)
{
  lock(toRetrieve)
  {
    Monitor.Wait(toRetrieve);
  }
}
Tags:
singalen: (Default)

Originally published at Fiberglass flowers. You can comment here or there.

I prefer first to identify program classes, then to think some time about its design, the longer the better.

Then, to prove my internal API idea, I make up code pieces by “wishful thinking”: how I wish the code to look, for it to be the most brief and to express core system objects and functionality, for classes to be least coupled etc etc.

I write them down to code, mock up classes, and try to make the code example work in unit test harness.

During this process I see more details: what does the API miss in order to make pieces work, especially in initialization, finalization, and whatever things were missed during initial design.

Finally I get a (huh) nearly working system core covered with readable unit tests.
I believe, not whe worst approach. Now, how can it be improved?
What did I miss?

Tags:
singalen: (Default)

Originally published at Fiberglass flowers. You can comment here or there.

  • Why FindAll() is not in IList? IDictionary or at least Dictionary? Are you pushing me to code for implementation?
  • Why WeakReference, but no WeakDictionary (get a nice one from Nick Guerrera)? WeakList? More?
  • Why ReadonlyCollection<T>, but no ReadonlyDictionary<K, V>?
  • (I can live with this one) Why Array.Length, but anyCollection.Count (thank Nick Guerrera again)?

…more to follow as I recall them…

Tags:
singalen: (Default)

Originally published at Fiberglass flowers. You can comment here or there.

Am I the only one who ran into .NET (or GDI?) bug with Pen (or Matrix) .ScaleTransform()?
When I scale a pen to certain width, and it has an anchor, anchor draws at inverse scale.
I might be missing something, but here’s my test. Pen scales proportionally to form width, for simplicity.

Here’s how it looks:

You see, the thinner the line, the bigger is the cap. Uh-oh.

My workaround should be here after some time.
Source code for interested ones:

private void Form1_Load(object sender, EventArgs e)
        {
            pen.StartCap = LineCap.SquareAnchor;
            //pen.EndCap = LineCap.ArrowAnchor;
            pen.EndCap = LineCap.DiamondAnchor;
        }

        private readonly Pen pen = new Pen(Color.Black, 2);

        private void Form1_Paint(object sender, PaintEventArgs e)
        {
            pen.ResetTransform();
            float scale = Width / 300.0F;
            pen.ScaleTransform(scale, scale);
            e.Graphics.DrawLine(pen, 10, 10, ClientRectangle.Width - 10, ClientRectangle.Height - 10);
        }

        private void Form1_Resize(object sender, EventArgs e)
        {
            Invalidate();
        }
Tags:
singalen: (Default)

Originally published at Fiberglass flowers. You can comment here or there.

Till two years ago, I didn’t know that C# 2.0 had closures (reference to Joe Walnes’ blog). Now declarative programming is slowly coming into fashion.
Reference taken from classicist. The latter has a bunch of short bliki-articles, all worth reading.

Please note variables visibility scope:

public List<Employee> HighPaid(List<Employee> emps) {

int threshold = 150;
return emps.FindAll(delegate(Employee e) {
return e.Salary > threshold;
});

}

interesting quotation about arguments against closures in Java - about memory allocation. Found again by Martin Fowler’s reference, in Guy Steele’s archive:
One of the early design principles of Java was that heap allocation occurs if and only if a construct involving the "new" keyword is executed. Adding full-blown closures violated this design principle. (Dynamic class loading also violated the principle, but users seemed to be comfortable with that, perhaps because they believed that "once the computation got going" no more heap allocation would occur.)
Other features for Java release 1.5 will perform certain kinds of autoboxing, which also violates the design principle. This fact will make it easier to argue for restoring full-blown support for closures in the future.

upd: But .NET doesn’t have an unordered container (set), and these cool Find()/FindAll() don’t exist in IList. Too bad.

Oh yes, and Python always had closures.

Tags:
singalen: (Default)

Originally published at Fiberglass flowers. Please leave any comments there.

Discussing Java vs C# coding conventions, I got an idea:
Geek code for a coding conventions.
Like:
-----BEGIN GEEK CODE-CODE BLOCK-----
GP:java,c,cpp,haskell
Off:2S P:N Name:Camel Flex:2
------END GEEK CODE-CODE BLOCK------

which means:

  • GP:java,c,cpp,haskell - geek of programming languages (listed);
  • Off:2S - prefer 2-space offsets;
  • P:N - place parentheses on new line (opposed to S - same);
  • Name:Camel - prefer camelCase;
  • Flex(0,1,2) - I’m flexible on this and can easily accept other’s conventions.

This will help others to see what you prefer an not (?) to start stupid holy arguing.