Форум программистов, компьютерный форум, киберфорум
Java SE (J2SE)
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.71/7: Рейтинг темы: голосов - 7, средняя оценка - 4.71
2 / 2 / 1
Регистрация: 14.11.2014
Сообщений: 38

Помощь в проектировании ООП решения

21.11.2016, 17:15. Показов 1571. Ответов 16

Студворк — интернет-сервис помощи студентам
Доброго времени суток комрады!
Пожалуйста помогите подобрать паттерн(если таковой вообще существует) или элегантное ООПшное решение для следующей ситуации:

есть массив фигур, ну для простоты
Java
1
ArrayList<Shape> MyListOfShapes
над элементами которого нужно выполнить какаую то математическую операцию с наперёд заданной мерой(операция и мера будет задаваться пользователем) и вот у меня огромная проблема в том, что поведение алгоритма зависит и от математической операции и от меры и если выбор математической операции я могу хоть как то реализовать через паттерн стратегия(насколько я его понимаю) то ещё одну зависимость от меры я немогу нормально полиморфно обыграть ибо может получится так что:


есть абстрактный класс Shape

Java
1
2
3
4
5
6
7
public abstract class Shape
{
    Measure m_measure;
 
    // чтобы можно было выбирать какую именно меру нужно вычислить
    public double calculateMeasure( Measure neobhodimaya_mera ){}
}
Представляющий некую абстрактную фигуру у которой обязательно должна быть какая то мера и тут первый затык какая именно мера зависит от фигуры, ну например у квадрата есть площадь, но не может быть объёма, в то время как у цилиндра может быть и площадь и объём, поэтому всё что я придумал это агрегацию в Shape со ссылкой на обобщённый интерфейс Measure от которого я потом буду наследоваться для конкретной меры.
Java
1
2
3
4
5
6
7
8
9
10
public interface Measure
 {
    
    public Measure getMeasure( Measure m)
    {
        m.Calculate();
 
    }
 
}
И может возникнуть такая ситуация что например пользователь выбирает сложение Объёмов(мер) элементов массива, а в массиве
Java
1
ArrayList<Shape> MyListOfShapes
могут находиться разные потомки от базового абстрактного класса Shape и может так случиться что у некоторых фигур например квадрата не существует такой меры как объём....
И всё это ставит меня в тупик...посему у меня к вам пару главных вопросов:

1)Как правильнее выстроить объектную модель (иерархию наследований или композиций \ агрегаций) ?
2)Как сделать удобный интерфейс у них?
чтобы всё это было удобно и расширяемо, ну можно было добавлять новые меры или математические операции не изменняя ранее написанный код...
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
21.11.2016, 17:15
Ответы с готовыми решениями:

Нужны задачи и решения с использованием концепций ООП
Здравствуйте, ищу примеры задач и решения с использованием 3-к концепций ООП ( в одной зачаче чтоб все 3 принципа были задействованы). На...

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

Написать программу решения квадратного уравнения с помощью ООП
Здравствуйте. Есть такое задание: написать программу решения квадратного уравнения с помощью ооп. Даже не знаю за что браться. Если не...

16
59 / 59 / 20
Регистрация: 21.03.2013
Сообщений: 186
21.11.2016, 19:59
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// создаем квадрат
Shape shape = new Square();
 
// добавляем его в список фигур
myListOfShapes.add(shape);
 
// создаем меру объема
Measure measure = new Measure("amount");
 
// проверяем
Shape shape = myListOfShapes.get(0);
if (shape instanceof Square && measure.getName == "amount") {
    throw new Exception("Square don't has amount");
}
0
2 / 2 / 1
Регистрация: 14.11.2014
Сообщений: 38
21.11.2016, 21:21  [ТС]
Да ты просто "гений" только вот вопрос не зря стоит в ОО Проектировании решения и\или применении паттернов(кстати модератор мне кажется ты зря переименовал тему)

а не в том как проверить является ли Shape одним из частных случаев реализации конкретной фигуры, с таким подходом ты быстро скатишься в большое кол-во IF'ов...*facepalm*
не говоря про то что твой код вообще не учитывает это:
Цитата Сообщение от iwk Посмотреть сообщение
поведение алгоритма зависит и от математической операции и от меры
0
Эксперт Java
3639 / 2971 / 918
Регистрация: 05.07.2013
Сообщений: 14,220
21.11.2016, 21:59
Че-то плохо понимаю, что ты делаешь.
Почему нельзя сделать два класса Shape2D и Shape3D например?
0
Эксперт Java
 Аватар для KEKCoGEN
2399 / 2224 / 565
Регистрация: 28.12.2010
Сообщений: 8,672
21.11.2016, 22:00
Цитата Сообщение от iwk Посмотреть сообщение
может так случиться что у некоторых фигур например квадрата не существует такой меры как объём....
и что тогда должно произойти? Исключение или пропуск фигуры?

Не уверен что верно понял что требуется. Вот решение той проблемы которую я понял. Когда определяется класс-наследник от Shape, он должен декларировать те Measures которые он поддерживает. Удобнее всего через конструктор базового объекта. Пример для Rectange

Java
1
2
3
4
5
6
7
public class Rectangle extends Shape{
    
    public Rectangle() {
        super(Arrays.asList(Measure.SQUARE));
    }
 
}
Соответственно класс Shape будет примерно таким

Java
1
2
3
4
5
6
7
8
9
10
11
12
public abstract class Shape {
    
    protected Collection<Measure> supportedMeasures;
 
    public Shape(Collection<Measure> supportedMeasures) {
        this.supportedMeasures = supportedMeasures;
    }
 
    public boolean supportsMeasure(Measure targetMeasure) {
        return supportedMeasures.contains(targetMeasure);
    }
}

В вызывающем классе, нам сначала надо отфильтровать те формы, которые поддерживают данный Measure

Java
1
2
3
List<Shape> shapes = Arrays.asList(new Rectangle(), new Sphere());
Measure targetMeasure = Measure.SQUARE;
List<Shape> supportedShapes = shapes.stream().filter(shape -> shape.supportsMeasure(targetMeasure)).collect(Collectors.toList());
Теперь можно выполнять нужную операцию не боясь что в коллекции будет объект не поддерживающий площадь.

Таким образом чтобы добавить новую фигуру, достаточно добавить её класс и определить какие меры она поддерживает. Это так же по желанию можно вынести в некий MeasuresManager который будет декларировать какая фигура что поддерживает.
1
2 / 2 / 1
Регистрация: 14.11.2014
Сообщений: 38
21.11.2016, 23:56  [ТС]
Цитата Сообщение от KEKCoGEN Посмотреть сообщение
и что тогда должно произойти? Исключение или пропуск фигуры?
в том то и "шиза" этого задания что насколько я понял то что должно произойти зависит от математического оператора... попробую переформулировать...

Должен произойти "гамотный пропуск" суть задания в том что нужно найти итого мер фигур в коллекции т.е. если я задаю знак умножения то перемножаются все меры фигур в коллекции и итого содержит произведения всех мер фигур для которых была справедлива эта мера и насколько я понимаю если для фигуры не справедлива эта мера мы её пропускаем и применяем математический оператор только ктем к которым справедлива мера

математический оператор применяется к каждому элементу массива(или коллекции - не важно) содержащему произвольную фигуру (они все разные могут быть какими угодно) и задаётся любая произвольная мера
и те фигуры которые поддерживают эту меру вычисляют эту меру по той формуле которая справедлива для этой конкретной фигуры И к этой вычесленной мере применяется математический оператор иии всё это должно быть расширяемым чтобы возможно было бы дописывать к коду новые математические операторы или меры без изменения уже существующего кода....
0
Эксперт Java
 Аватар для KEKCoGEN
2399 / 2224 / 565
Регистрация: 28.12.2010
Сообщений: 8,672
22.11.2016, 00:08
iwk, выше решение, отвещающее этим критериям.
0
2 / 2 / 1
Регистрация: 14.11.2014
Сообщений: 38
22.11.2016, 00:20  [ТС]
Цитата Сообщение от KEKCoGEN Посмотреть сообщение
iwk, выше решение, отвещающее этим критериям.
Большое спасибо, что то я совсем обложился всякими наследованиями\агрегациями и не подумал что вместо того чтобы строить хитрожопую архитектуру нужно просто отфильтровать коллекцию и из отфильтрованной коллекции сделать вторую содержащую только то что нужно а там уже и работать с ней....
0
Эксперт Java
 Аватар для KEKCoGEN
2399 / 2224 / 565
Регистрация: 28.12.2010
Сообщений: 8,672
22.11.2016, 00:28
Цитата Сообщение от KEKCoGEN Посмотреть сообщение
Удобнее всего через конструктор базового объекта.
на самом деле это не совсем верно. Удобней через абстрактный метод в Shape::getSupportedMeasures(). Таким образом тот, кто создает класс будет должен явно прописать поддерживающиеся меры и сами меры ненадо будет хранить как мембер класса Shape.
0
2 / 2 / 1
Регистрация: 14.11.2014
Сообщений: 38
22.11.2016, 01:01  [ТС]
Цитата Сообщение от KEKCoGEN Посмотреть сообщение
сами меры ненадо будет хранить как мембер класса Shape.
т.е. как агрегацию?, ну тупо удалить ссылку на меру из абстрактного класса Shape?
Цитата Сообщение от KEKCoGEN Посмотреть сообщение
Таким образом тот, кто создает класс будет должен явно прописать поддерживающиеся меры
Эмм и как бы это покрасивее сделать?
Java
1
2
3
4
public abstract class Shape
{
   public ArrayList getSupportedMeasures() {}
}

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Rectangle extends Shape
{
 
    ArrayList supportedMeasures = New ArrayList();
 
    Rectangle()
   { 
       supportedMeasures.add(square);
       supportedMeasures.add(otherMeasure);
   }
    
    public ArrayList getSupportedMeasures(){
        return  supportedMeasures;
    }
или есть способ покрасивее?

Просто сильно смущает что для КАЖДОЙ фигуры это нужно будет делать вручную....хотя если подумать то другого выхода наверное просто и не существует..
0
Эксперт Java
 Аватар для KEKCoGEN
2399 / 2224 / 565
Регистрация: 28.12.2010
Сообщений: 8,672
22.11.2016, 09:59
iwk,

Java
1
2
3
4
public abstract class Shape
{
   public abstract Collection<Measure> getSupportedMeasures();
}

Java
1
2
3
4
5
6
public class Rectangle extends Shape
{
    @Override
    public Collection<Measure> getSupportedMeasures(){
        return  Arrays.asList(square, otherMeasure);
    }
Цитата Сообщение от iwk Посмотреть сообщение
Просто сильно смущает что для КАЖДОЙ фигуры это нужно будет делать вручную
Понятно что каждая фигура должна декларировать какие меры она поддерживает. Этоу информации кроме неё никто не может знать.
1
2 / 2 / 1
Регистрация: 14.11.2014
Сообщений: 38
26.11.2016, 23:26  [ТС]
KEKCoGEN,
Долго ломал голову, решение подсказали за что большое спасибо, но как то по мне оно слишком прямолинейное слишком мне кажется топорное...заходить в элемент коллекции там прогонять внутренний массив элемента на соответствие заданной мере... мне кажеться не совсем "стиль ООП"...
Хотелось бы чтобы была какая то хитрая иерархия наследований благодаря которой с помощью полиморфизма выбирались из коллекции те классы которые поддерживают конкретную меру и в зависимости и от меры и от фигуры(а у фигуры может быть много мер) для каждой фигуры посвоему рассчитывалась эта мера , ну хоть что то отдалённое есь в паттерне стратегия:(но мне нужно что то покруче)

код упрощён для наглядности
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
public interface  Operator
 {
     public double execute(){}  
}
 
public class addition implements Operator
{
        public double execute( ArrayList<Shape> shapesList, Measure m )
     {
         double tmp_res = 0.0;
 
         while( shapesList.hasnext() )
        {
                   tmp_res +=  shapesList.calculateMeasure( m );
         }
     }
}
 
public class GroupOfShapes
{
   Operator m_operator;
   Measure m_measure;
   ArrayList<Shape> shapesList;
   
   public void addFiure(/*bla bla*/)
{
   /*bla bla bla*/
}   
 
   public void setOperator( Operator MathOperator)
   {
       m_operator = MathOperator;
    }
 
    public void setMeasure( Measure m)
   {
       m_measure = m;
    }
 
     public void Calculate()
     { 
         m_operator.execute( ArrayList<Shape> shapesList, m_measure );
     }
   
}
 
public static void main (String[] args) throws java.lang.Exception
{
 
     GroupOfShapes mygroup = new GroupOfShapes();
     mygroup.addFigure(/*bla bla*/);
 
//И ВОТ ЕСЛИ ТУТ ВСЁ ПОНЯТНО что можно вставить любой математический оператор типа new Multiplication и т.д.
// т.к. есть общий интерфейс Operator и унаследовавшись от него мы получаем гибкость и возможность   
// реализации любого алгоритма  который нам нужен и можем спокойно расширять кол-во алгоритмов 
     mygroup.setOperator( new addition );
 
//ТО КАК сделать тоже самое для меры чтобы с помощью полиморфизма выбирались из коллекции те классы
//которые поддерживают конкретную меру и в зависимости и от меры и от фигуры для каждой фигуры  
//по своему рассчитывалась эта мера через ... я немогу это решить и у меня взрывается мозг как
// выстроить такую архитектуру наследований?
               
}
0
2 / 2 / 1
Регистрация: 14.11.2014
Сообщений: 38
01.12.2016, 23:11  [ТС]
вопрос всё ещё актуален
0
Эксперт функциональных языков программированияЭксперт Java
 Аватар для korvin_
4575 / 2774 / 491
Регистрация: 28.04.2012
Сообщений: 8,779
02.12.2016, 01:52
iwk, если не ошибаюсь, в таких случаях используют паттерн Visitor. А так, мультиметоды бы подошли, в Clojure они есть, например.

Добавлено через 59 минут
Навскидку (просто в лоб, не знаком с Visitor вообще):

Java
1
2
3
4
5
@FunctionalInterface
public interface Measure {
 
    double calculate();
}
Java
1
2
3
4
5
6
import java.util.Optional;
 
public interface Shape {
 
    Optional<Measure> getMeasure(String name);
}
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
 
public final class StandardMeasures {
 
    private static final Set<String> ALL = new HashSet<>();
 
    public static final String SIDE = add("side");
    public static final String RADIUS = add("radius");
    public static final String SQUARE = add("square");
 
    public static Collection<String> all() {
        return Collections.unmodifiableSet(ALL);
    }
 
    private static String add(String s) {
        ALL.add(s);
        return s;
    }
 
    private StandardMeasures() {
    }
}
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.*;
 
public final class Square implements Shape, ShapeMeasureTrait<Square> {
 
    private static final Map<String, Function<Square, Measure>> MEASURES;
 
    static {
        MEASURES = new HashMap<>();
        MEASURES.put(StandardMeasures.SIDE, square -> square::side);
        MEASURES.put(StandardMeasures.SQUARE, square -> square::square);
    }
 
    private final double side;
 
    public Square(double side) {
        this.side = side;
    }
 
    @Override
    public Optional<Measure> getMeasure(String name) {
        return getMeasure(MEASURES, this, name);
    }
 
    public double side() {
        return side;
    }
 
    public double square() {
        return side * side;
    }
}
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
 
public final class Circle implements Shape, ShapeMeasureTrait<Circle> {
 
    private static final Map<String, Function<Circle, Measure>> MEASURES;
 
    static {
        MEASURES = new HashMap<>();
        MEASURES.put(StandardMeasures.RADIUS, circle -> circle::radius);
        MEASURES.put(StandardMeasures.SQUARE, circle -> circle::square);
    }
 
    private final double radius;
 
    public Circle(double radius) {
        this.radius = radius;
    }
 
    @Override
    public Optional<Measure> getMeasure(String name) {
        return getMeasure(MEASURES, this, name);
    }
 
    public double radius() {
        return radius;
    }
 
    public double square() {
        return Math.PI * radius * radius;
    }
}
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.DoubleStream;
 
public final class Main {
 
    public static void main(String[] args) {
        final List<Shape> shapes = new ArrayList<>();
        shapes.add(new Square(4));
        shapes.add(new Circle(3));
        for (final String measure : StandardMeasures.all()) {
            print(System.out, measure, calculateMeasure(shapes, measure));
        }
    }
 
    private static DoubleStream calculateMeasure(List<Shape> shapes, String measure) {
        return shapes.stream()
                .map(s -> s.getMeasure(measure))
                .filter(Optional::isPresent)
                .map(Optional::get)
                .mapToDouble(Measure::calculate);
    }
 
    private static void print(PrintStream out, String measure, DoubleStream values) {
        out.println();
        out.println(measure);
        values.forEach(out::println);
    }
}
1
2 / 2 / 1
Регистрация: 14.11.2014
Сообщений: 38
04.12.2016, 13:13  [ТС]
korvin_, спасибо за труды, буду разбираться
скажите по вашему замыслу тут должен быть в HashSet<> String ?
Цитата Сообщение от korvin_ Посмотреть сообщение
private static final Set<String> ALL = new HashSet<>();
и что это за интерфейс ShapeMeasureTrait он у вас тут не описан...
0
Эксперт функциональных языков программированияЭксперт Java
 Аватар для korvin_
4575 / 2774 / 491
Регистрация: 28.04.2012
Сообщений: 8,779
04.12.2016, 18:29
Цитата Сообщение от iwk Посмотреть сообщение
по вашему замыслу тут должен быть в HashSet<> String ?
В смысле?

Цитата Сообщение от iwk Посмотреть сообщение
и что это за интерфейс ShapeMeasureTrait он у вас тут не описан...
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
 
interface ShapeMeasureTrait<T extends Shape> {
 
    default Optional<Measure> getMeasure(Map<String, Function<T, Measure>> measures, T object, String measure) {
        final Function<T, Measure> m = measures.get(measure);
        if (m == null) {
            return Optional.empty();
        }
        return Optional.of(m.apply(object));
    }
}
1
2 / 2 / 1
Регистрация: 14.11.2014
Сообщений: 38
04.12.2016, 23:05  [ТС]
Цитата Сообщение от korvin_ Посмотреть сообщение
В смысле?
Аааа, да короче у меня в проекте стояла старая джава которая не поддерживала diamond types поэтому писало ошибку в
Java
1
private static final Set<String> ALL = new HashSet<>();
и нужно было писать вот так:
Java
1
private static final Set<String> ALL = new HashSet<String>();
я то на старой джаве 6й сидел а тут у вас и лямбды и О май гад уже и в интерфейсе можно писать реализацию метода ахренеть...даа...
Цитата Сообщение от korvin_ Посмотреть сообщение
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
 
interface ShapeMeasureTrait<T extends Shape> {
 
    default Optional<Measure> getMeasure(Map<String, Function<T, Measure>> measures, T object, String measure) {
        final Function<T, Measure> m = measures.get(measure);
        if (m == null) {
            return Optional.empty();
        }
        return Optional.of(m.apply(object));
    }
}
ещё раз спасибо пойду учить 8ю джаву...
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
04.12.2016, 23:05
Помогаю со студенческими работами здесь

Нужны советы в поиске способов решения курсовой по ООП
2 курс, тему для курсовой по ООП разрешили выбрать самостоятельно. пришла на ум идея сделать десктоп приложение для просмотра расписания...

Нужна помощь в реализации методов решения задачи.
По учебе надо сделать программу, которая должна: Реализовать методы решения задачи F(x1,x2,...,xn)-&gt;min; Методами прямого поиска...

Помощь решении задачи по Теории принятия решения
Дорогие друзья, помогите мне решить задачу по теории решения. Очень нужно до завтра, спасибо :) Задача Джек выбирает...

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

О проектировании светодиодной гирлянды...
Вот хочу замутить подстветку в потолок на светодиодах. Технические характеристики: Светодиод типа RGB; Напряжение 2.1 3.2 3.2; Ток...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
17
Ответ Создать тему
Новые блоги и статьи
Символьное дифференцирование
igorrr37 13.02.2026
/ * Логарифм записывается как: (x-2)log(x^2+2) - означает логарифм (x^2+2) по основанию (x-2). Унарный минус обозначается как ! */ #include <iostream> #include <stack> #include <cctype>. . .
Камера Toupcam IUA500KMA
Eddy_Em 12.02.2026
Т. к. у всяких "хикроботов" слишком уж мелкий пиксель, для подсмотра в ESPriF они вообще плохо годятся: уже 14 величину можно рассмотреть еле-еле лишь на экспозициях под 3 секунды (а то и больше),. . .
И ясному Солнцу
zbw 12.02.2026
И ясному Солнцу, и светлой Луне. В мире покоя нет и люди не могут жить в тишине. А жить им немного лет.
«Знание-Сила»
zbw 12.02.2026
«Знание-Сила» «Время-Деньги» «Деньги -Пуля»
SDL3 для Web (WebAssembly): Подключение Box2D v3, физика и отрисовка коллайдеров
8Observer8 12.02.2026
Содержание блога Box2D - это библиотека для 2D физики для анимаций и игр. С её помощью можно определять были ли коллизии между конкретными объектами и вызывать обработчики событий столкновения. . . .
SDL3 для Web (WebAssembly): Загрузка PNG с прозрачным фоном с помощью SDL_LoadPNG (без SDL3_image)
8Observer8 11.02.2026
Содержание блога Библиотека SDL3 содержит встроенные инструменты для базовой работы с изображениями - без использования библиотеки SDL3_image. Пошагово создадим проект для загрузки изображения. . .
SDL3 для Web (WebAssembly): Загрузка PNG с прозрачным фоном с помощью SDL3_image
8Observer8 10.02.2026
Содержание блога Библиотека SDL3_image содержит инструменты для расширенной работы с изображениями. Пошагово создадим проект для загрузки изображения формата PNG с альфа-каналом (с прозрачным. . .
Установка Qt-версии Lazarus IDE в Debian Trixie Xfce
volvo 10.02.2026
В общем, достали меня глюки IDE Лазаруса, собранной с использованием набора виджетов Gtk2 (конкретно: если набирать текст в редакторе и вызвать подсказку через Ctrl+Space, то после закрытия окошка. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru