#urlshorter Сначала хочу сказать, что открытый исходный код хостится на @红薯's платформе.
Предыдущий раз я писал статью «Простое проектирование и реализация преобразования длинной URL в короткую», которая была написана довольно仓促,缺乏设计,因此方案也不完整。 Увидев энтузиазм, чтение и сохранение этой статьи, я глубоко обеспокоен тем, что написал такую простую статью, поэтому появилась эта новая статья и её открытый исходный код.
Действительно, на этот раз было затрачено значительное количество времени на дизайн и написание кода, примерно один день.
Сначала рассмотрим ключевые моменты:
package org.tinygroup.shorter;
/**
* Генератор случайных строк
* Created by luoguo on 2017/3/24.
*/
public interface StringGenerator {
String generate(String url);
void setLength(int length);
}
Метод setLength
используется для установки длины генерируемой строки, метод generate
используется для генерации соответствующих коротких ссылок.
В предыдущей статье некоторые люди критиковали логику генерации за её недостаточность, но на этот раз многое улучшено, и вы можете свободно выбирать способ, который вам нравится.
Конечно, чтобы сделать всё удобнее для ленивых, также предоставлено стандартное выполнение, которое просто перенёс алгоритм из предыдущей версии.
/**
* Стандартный генератор случайных строк
*
* Created by luoguo on 2017/3/24.
*/
public class StringGeneratorRandom implements StringGenerator {
public static final char[] VALID_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890".toCharArray();
private static final Random RANDOM = new Random(System.currentTimeMillis());
private int length = 4;
public StringGeneratorRandom() {}
public StringGeneratorRandom(int length) {
setLength(length);
}
}```java
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
public String generate(int seed) {
char[] shortUrl = new char[length];
for (int i = 0; i < length; i++) {
shortUrl[i] = VALID_CHARS[seed % VALID_CHARS.length];
seed = RANDOM.nextInt(Integer.MAX_VALUE) % VALID_CHARS.length;
}
return new String(shortUrl);
}
}
``````markdown
### Генерация случайной строки
Алгоритм очень простой, поэтому подробное объяснение не требуется.
#### Интерфейс генератора коротких ссылок
```java
/**
*
* Created by Luoguo on March 24, 2017.
*/
public interface UrlShorterGenerator<T extends ShorterGetter> {
/**
* Генерирует объект короткой ссылки
*
* @param url
* @return
*/
T generate(String url);
}
Здесь всего один метод, который позволяет создать короткую ссылку на основе длинной URL. Некоторые могут спросить, почему здесь используется тип T
, если короткая ссылка должна быть просто строкой? Ответ заключается в том, что реальные сценарии использования требуют различных вариантов генерации коротких ссылок — от простых до сложных, таких как ссылки с паролями, ограничениями по количеству запросов и временными рамками доступности.
Поэтому был создан интерфейс ShorterGetter
.
/**
* Для получения короткой ссылки
* Created by Luoguo on March 24, 2017.
*/
public interface ShorterGetter<T> {
String getShorter();
}
Этот интерфейс требует только возврата одной строки короткой ссылки, а остальное может быть реализовано по желанию.
В проекте есть пять способов создания коротких ссылок:
#### Простая короткая ссылка
```java
/**
* Возвращает короткий код и пароль
* Создан Луогуо 24 марта 2017 года.
*/
public class ShorterString implements ShorterGetter<String> {
private String shorter;
public ShorterString() {}
public ShorterString(String shorter) {
setShorter(shorter);
}
public String getShorter() {
return shorter;
}
public void setShorter(String shorter) {
this.shorter = shorter;
}
}
/**
* Хранит короткую ссылку и пароль
* Создан Луогуо 24 марта 2017 года.
*/
public class ShorterWithPassword implements ShorterGetter<ShorterWithPassword> {
private String shorter;
private String password;
public ShorterWithPassword() {}
/**
* Хранение короткого адреса и времени жизни
* Создано Луогуо в 2017/03/24
*/
public class ShorterWithPeriod implements ShorterGetter<ShorterWithPeriod> {
private String shorter;
private long period;
public ShorterWithPeriod() {}
public ShorterWithPeriod(String shorter, long period) {
setShorter(shorter);
setPeriod(period);
}
public String getShorter() {
return shorter;
}
public void setShorter(String shorter) {
this.shorter = shorter;
}
public long getPeriod() {
return period;
}
public void setPeriod(long period) {
this.period = period;
}
}
/**
* Хранение короткого адреса, времени жизни и количества использований
* Создано Луогуо в 2017/03/24
*/
public class ShorterWithPeriodAndTimes implements ShorterGetter<ShorterWithPeriodAndTimes> {
private String shorter;
private long period;
private long times;
``````java
/**
* Для хранения строковых коротких адресов
* Created by Luoguo on March 24, 2017
*/
public interface ShorterStorage<T extends ShorterGetter> {
String get(String shortAddress);
void clean(String url);
void cleanShorter(String shortAddress);
void save(String url, T shortAddress);
void clean();
}
/**
* Created by Luoguo on March 24, 2017
*/
public class ShorterStorageMemory<T extends ShorterGetter> implements ShorterStorage<T> {
/**
* Хранение shortAddress, url
*/
private final Map<ShorterGetter, String> shortAddressToURLMap = new ConcurrentHashMap<>();
/**
* Хранение url, shortAddress
*/
private final Map<String, ShorterGetter> urlToShortAddressMap = new ConcurrentHashMap<>();
/**
* Хранение shortAddress, shortAddress, url
*/
private final Map<String, ShorterGetter> shortAddressToShortAddressMap = new ConcurrentHashMap<>();
}
@Override
public String get(String shortAddressKey) {
ShorterGetter shortAddress = shortAddressToShortAddressMap.get(shortAddressKey);
if (shortAddress != null) {
return shortAddressToURLMap.get(shortAddress);
}
return null;
}
@Override
public void clean(String url) {
ShorterGetter shortAddress = urlToShortAddressMap.get(url);
if (shortAddress != null) {
urlToShortAddressMap.remove(url);
shortAddressToURLMap.remove(shortAddress);
shortAddressToShortAddressMap.remove(shortAddress.getShortAddress());
}
}
@Override
public void cleanShorter(String shortAddressKey) {
ShorterGetter shortAddress = shortAddressToShortAddressMap.get(shortAddressKey);
if (shortAddress != null) {
urlToShortAddressMap.remove(shortAddressToURLMap.get(shortAddress));
shortAddressToURLMap.remove(shortAddress);
shortAddressToShortAddressMap.remove(shortAddress.getShortAddress());
}
}
@Override
public void save(String url, T shortAddress) {
urlToShortAddressMap.put(url, shortAddress);
shortAddressToURLMap.put(shortAddress, url);
shortAddressToShortAddressMap.put(shortAddress.getShortAddress(), shortAddress);
}
@Override
public void clean() {
shortAddressToURLMap.clear();
shortAddressToShortAddressMap.clear();
urlToShortAddressMap.clear();
}
}
Если требуется, вы можете реализовать своё собственное хранилище базы данных, Redis или любой другой способ, который вам нужен. До этого момента основные проблемы были решены; теперь рассмотрим реализацию генератора.
Примечание: Здесь отсутствует контроль за ограничениями, который следует включить при реальном использовании. Например, при получении данных проверьте наличие доступности, если она отсутствует, верните null
.
## С генерацией по времени
java
/**
Генерирует сокращённые ссылки с ограничением по времени использования
Создано LuoGou 24 марта 2017 года / public class UrlShorterGeneratorLimitPeriod implements UrlShorterGenerator { private StringGenerator generator; private ShorterStorage shorterStorage; /*
public StringGenerator getGenerator() { return generator; }
public void setGenerator(StringGenerator generator) { this.generator = generator; }
public ShorterStorage getShorterStorage() { return shorterStorage; }
public void setShorterStorage(ShorterStorage shorterStorage) { this.shorterStorage = shorterStorage; }
public long getPeriod() { return period; }
public void setPeriod(long period) { this.period = period; }
@Override public ShorterWithPeriod generate(String url) { String shorter = generator.generate(url); while (shorterStorage.get(shorter) != null) { shorter = generator.generate(url); } ShorterWithPeriod shorterWithPeriod = new ShorterWithPeriod(shorter, period); shorterStorage.save(url, shorterWithPeriod); return shorterWithPeriod; } }
## Генератор с ограничением по количеству запросов
```java
/**
* Генерирует сокращённые ссылки с ограничением по количеству запросов
* Создано LuoGou 24 марта 2017 года
*/
public class UrlShorterGeneratorLimitTimes implements UrlShorterGenerator<ShorterWithTimes> {
private StringGenerator generator;
private ShorterStorage<ShorterWithTimes> shorterStorage;
/**
* Ограничение по количеству запросов
*/
private long times;
public StringGenerator getGenerator() {
return generator;
}
/**
Класс для генерации короткого URL с ограничением по количеству использований и времени */ public class UrlShorterGeneratorLimitPeriodAndTimes implements UrlShorterGenerator { private StringGenerator generator; private ShorterStorage shorterStorage;
/**
/**
public StringGenerator getGenerator() { return generator; }
public void setGenerator(StringGenerator generator) { this.generator = generator; }
public ShorterStorage getShorterStorage() { return shorterStorage; }
public void setShorterStorage(ShorterStorage shorterStorage) { this.shorterStorage = shorterStorage; }
public long getTimes() { return times; }
public void setTimes(long times) { this.times = times; }
public long getPeriod() { return period; }
public void setPeriod(long period) { this.period = period; }
public ShorterWithPeriodAndTimes generate(String url) { String shorter = generator.generate(url); while (shorterStorage.get(shorter) != null) { shorter = generator.generate(url); }
## Пример
### Генерация фиксированной длины короткого адреса
```java
/**
* Создано LuoGou на 2017/3/24.
*/
public class UrlShorterGeneratorSimpleTest {
@Test
public void generate() throws Exception {
for (int i = 4; i <= 8; i++) {
UrlShorterGeneratorSimple simple = new UrlShorterGeneratorSimple();
simple.setGenerator(new StringGeneratorRandom(i));
simple.setShorterStorage(new ShorterStorageMemory<>());
for (int j = 0; j < 5; j++) {
String shorter = simple.generate("").getShorter();
assert shorter.length() == i;
System.out.println(shorter);
}
}
}
}
```### Короткий адрес с паролем
```java
/**
* Создано luoguo на 2017/3/25.
*/
public class UrlShorterGeneratorWithPasswordTest {
@Test
public void generate() throws Exception {
for (int i = 4; i <= 8; i++) {
UrlShorterGeneratorWithPassword withPassword = new UrlShorterGeneratorWithPassword();
withPassword.setShorterGenerator(new StringGeneratorRandom(i));
withPassword.setPasswordGenerator(new StringGeneratorRandom(4));
withPassword.setShorterStorage(new ShorterStorageMemory<>());
for (int j = 0; j < 5; j++) {
ShorterWithPassword shorter = withPassword.generate("");
assert shorter.getShorter().length() == i;
System.out.printf("%s %s\n", shorter.getShorter(), shorter.getPassword());
}
}
}
}
Результат выполнения:
0yET AYOf
37w1 MBjA
SDMg B72n
BdTv KAwd
KQ1w iwiP
mZAVV u8Zx
rdUlH 5a7T
uZQ5i j38x
PUfY0 kfH3
MG3iW bkHO
Ea4TJr Nt8v
2fycK1 6eF3
Q6arED rEID
wc9yf1 kcGr
uGs5uu vKhA
upsmJXt 1IIl
6feAOFV Afqm
j0qPXCG R9VN
2We0RqM 9722
SdgG0Yy tS6e
ZDUyOeeg kiTh
Хаха, результат полностью соответствует моим ожиданиям.
Эта версия значительно превзошла предыдущую, с более рациональной архитектурой, расширенными функциями и большей гибкостью. Уверены, что она будет полезна для всех, кто нуждается в таких решениях.
Из-за короткого времени разработки, в проекте могут содержаться ошибки, недочёты или дизайнерские недоработки, поэтому мы приглашаем вас отправлять pull requests для совместной доработки.
Git адрес: https://git.oschina.net/tinyframework/urlshorter.gitДля просмотра других интересных публикаций, перейдите на блог "Успешный Блог". Подписывайтесь на наш канал, если вам интересны наши последние новости.
Если вы считаете этот проект полезным или ценным, не забудьте сделать пожертвование!
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Комментарии ( 0 )